Предупреждение: Нижеследующее многие сочтут извращением с C#. Возможно конечно это и не так, но я Вас предупредил:).
В Ruby есть интересная особенность для перехвата вызовов несуществующих методов. В таких случаях Ruby вызывает метод вашего класса с названием «method_missing». Автор этого текста показывал пример этого на IronRuby в другой статье.
Для динамических языков это может быть нормой, но в статически типизированных языках такого не бывает.
Не было до текущего момента!(Барабанная дробь)
В C# 4.0 вводится новое ключевое слово dynamic, которое добавляет динамические свойства к статически типизированному языку. Не бойтесь, никто не заставит вас это использовать(кроме автора этой статьи). На самом деле, оригинальная цель этого — сделать interop с COM намного проще. Но это не тема этой статьи, посмотрим как можно повеселиться с этой фичей:)
Автор решил попытаться реализовать нечто вроде method_missing.
Первое что он написал — это простейший динамический словарь который использует свойства для добавления и получения значений в/из словаря используя имя свойства как ключ. Вот пример использования:
Это не так уж плохо выглядет, и код простой. Чтобы сделать динамический объект, у нас есть выбор или реализовать интерфейс IDynamicMetaObjectProvider или просто наследоваться от DynamicObject. Автор выбрал второй подход потому что это быстрее и проще сделать. Вот код:
Всё что автор здесь делает — перегружает метод TrySetMember который вызывается при попытке установить свойство динамического объекта.Автор берет имя поля и использует его как ключ словаря. Так же автор мерегружает метод TryGetMember чтобы возвращать значения из словаря.
Стоит отметить, в Ruby на самом деле нету свойств и методов. Всё является методом, надо только позаботиться о method_missing. Не существует field_missing метода, например. В C# есть разница, поэтому есть другой метод который мы можем перегрузить — TryInvokeMember чтобы обрабатывать динамические вызовы.
Какой хаос можно создать с помощью этого в MVC?
Т.к. автор любитель явно типизированного view data в ASP.NET MVC, он любит вставлять некоторые вспомогательные данные в ViewDataDictionary. Конечно это добавляет синтаксический оверхед, который он любит уменьшать. Вот что было:
Похоже сюда можно впихнуть динамический словарь.
Прежде чем показывать код, сначала покажу результат. Автор создал новое свойство для Controller и ViewPage с названием Data (вместо ViewData), просто чтобы было покороче и изза нежелания называть его VD.
Вот код контроллера:
Заметьте что Message и Body на самом деле не свойства Data, это ключи словаря. Эта запись эквивалетна ViewData[«Message»] = "…".
В этом представлении автор создал собственные правила где все доступ к данным будут закодированы html (Html.Encode, хз как это более адекватно перевести) если не использовать знак подчеркивания.
Имейте ввиду что Data.Message здесь эквивалентна ViewData[«Message»].
Вот скриншот конечного результата:
Вот как автор это сделал. Сначала был создан класс DynamicViewData:
Если приглядеться, вы заметите что автор делает некие махинации в TryGetMember. Это то, где он проверяет на наличие знака подчеркивания перед именем свойства, и в случае наличия — кодирует текст как html (Естественно знак подчеркивания при этом из названия ключа для словаря убирается).
Дальше автор создает собственный DynamicController:
и DynamicViewPage (оба из которых используют новый класс DynamicViewData):
В папке Views автор обновил web.config чтобы сделать DynamicViewPage базовым классом по умолчанию для представлений вместо ViewPage. Это можно изменить устанавливая аттрибут pageBaseType элемента .
Автор надеется что это было весело, и прекрасно осознает, что хоть некоторые и будут ругаться за такое использование кейворда dynamic, другие могут увидеть потенциал в этой новой фишки.
В Ruby есть интересная особенность для перехвата вызовов несуществующих методов. В таких случаях Ruby вызывает метод вашего класса с названием «method_missing». Автор этого текста показывал пример этого на IronRuby в другой статье.
Для динамических языков это может быть нормой, но в статически типизированных языках такого не бывает.
Не было до текущего момента!(Барабанная дробь)
В C# 4.0 вводится новое ключевое слово dynamic, которое добавляет динамические свойства к статически типизированному языку. Не бойтесь, никто не заставит вас это использовать(кроме автора этой статьи). На самом деле, оригинальная цель этого — сделать interop с COM намного проще. Но это не тема этой статьи, посмотрим как можно повеселиться с этой фичей:)
Автор решил попытаться реализовать нечто вроде method_missing.
Первое что он написал — это простейший динамический словарь который использует свойства для добавления и получения значений в/из словаря используя имя свойства как ключ. Вот пример использования:
- static void Main(string[] args)
- {
- dynamic dict = new DynamicDictionary();
- dict.Foo = "Some Value"; // Compare to dict["Foo"] = "Some Value";
- dict.Bar = 123; // Compare to dict["Bar"] = 123;
- Console.WriteLine("Foo: {0}, Bar: {1}", dict.Foo, dict.Bar);
- Console.ReadLine();
- }
-
* This source code was highlighted with Source Code Highlighter.
Это не так уж плохо выглядет, и код простой. Чтобы сделать динамический объект, у нас есть выбор или реализовать интерфейс IDynamicMetaObjectProvider или просто наследоваться от DynamicObject. Автор выбрал второй подход потому что это быстрее и проще сделать. Вот код:
-
-
- public class DynamicDictionary : DynamicObject
- {
- Dictionary<string, object>
- _dictionary = new Dictionary<string, object>();
- public override bool TrySetMember(SetMemberBinder binder, object value)
- {
- _dictionary[binder.Name] = value;
- return true;
- }
- public override bool TryGetMember(GetMemberBinder binder,
- out object result)
- {
- return _dictionary.TryGetValue(binder.Name, out result);
- }
- }
-
-
* This source code was highlighted with Source Code Highlighter.
Всё что автор здесь делает — перегружает метод TrySetMember который вызывается при попытке установить свойство динамического объекта.Автор берет имя поля и использует его как ключ словаря. Так же автор мерегружает метод TryGetMember чтобы возвращать значения из словаря.
Стоит отметить, в Ruby на самом деле нету свойств и методов. Всё является методом, надо только позаботиться о method_missing. Не существует field_missing метода, например. В C# есть разница, поэтому есть другой метод который мы можем перегрузить — TryInvokeMember чтобы обрабатывать динамические вызовы.
Какой хаос можно создать с помощью этого в MVC?
Т.к. автор любитель явно типизированного view data в ASP.NET MVC, он любит вставлять некоторые вспомогательные данные в ViewDataDictionary. Конечно это добавляет синтаксический оверхед, который он любит уменьшать. Вот что было:
- // store in ViewData
- ViewData["Message"] = "Hello World";
-
- // pull out of view data
- <%= Html.Encode(ViewData["Message"]) %>
* This source code was highlighted with Source Code Highlighter.
Похоже сюда можно впихнуть динамический словарь.
Прежде чем показывать код, сначала покажу результат. Автор создал новое свойство для Controller и ViewPage с названием Data (вместо ViewData), просто чтобы было покороче и изза нежелания называть его VD.
Вот код контроллера:
- public ActionResult Index() {
- Data.Message = "<cool>Welcome to ASP.NET MVC!</cool> (encoded)";
- Data.Body = "<strong>This is not encoded</strong>.";
-
- return View();
- }
* This source code was highlighted with Source Code Highlighter.
Заметьте что Message и Body на самом деле не свойства Data, это ключи словаря. Эта запись эквивалетна ViewData[«Message»] = "…".
В этом представлении автор создал собственные правила где все доступ к данным будут закодированы html (Html.Encode, хз как это более адекватно перевести) если не использовать знак подчеркивания.
- <asp:Content ContentPlaceHolderID="MainContent" runat="server">
- <h2><%= Data.Message %></h2>
- <p>
- <%= Data._Body %>
- </p>
- </asp:Content>
* This source code was highlighted with Source Code Highlighter.
Имейте ввиду что Data.Message здесь эквивалентна ViewData[«Message»].
Вот скриншот конечного результата:
Вот как автор это сделал. Сначала был создан класс DynamicViewData:
- public class DynamicViewData : DynamicObject {
- public DynamicViewData(ViewDataDictionary viewData) {
- _viewData = viewData;
- }
- private ViewDataDictionary _viewData;
-
- public override bool TrySetMember(SetMemberBinder binder, object value) {
- _viewData[binder.Name] = value;
- return true;
- }
-
- public override bool TryGetMember(GetMemberBinder binder,
- out object result) {
- string key = binder.Name;
- bool encoded = true;
- if (key.StartsWith("_")) {
- key = key.Substring(1);
- encoded = false;
- }
- result = _viewData.Eval(key);
- if (encoded) {
- result = System.Web.HttpUtility.HtmlEncode(result.ToString());
- }
- return true;
- }
- }
* This source code was highlighted with Source Code Highlighter.
Если приглядеться, вы заметите что автор делает некие махинации в TryGetMember. Это то, где он проверяет на наличие знака подчеркивания перед именем свойства, и в случае наличия — кодирует текст как html (Естественно знак подчеркивания при этом из названия ключа для словаря убирается).
Дальше автор создает собственный DynamicController:
- public class DynamicController : Controller {
- public dynamic Data {
- get {
- _viewData = _viewData ?? new DynamicViewData(ViewData);
- return _viewData;
- }
- }
- dynamic _viewData = null;
- }
* This source code was highlighted with Source Code Highlighter.
и DynamicViewPage (оба из которых используют новый класс DynamicViewData):
- public class DynamicViewPage : ViewPage {
- public dynamic Data {
- get {
- _viewData = _viewData ?? new DynamicViewData(ViewData);
- return _viewData;
- }
- }
- dynamic _viewData = null;
- }
* This source code was highlighted with Source Code Highlighter.
В папке Views автор обновил web.config чтобы сделать DynamicViewPage базовым классом по умолчанию для представлений вместо ViewPage. Это можно изменить устанавливая аттрибут pageBaseType элемента .
Автор надеется что это было весело, и прекрасно осознает, что хоть некоторые и будут ругаться за такое использование кейворда dynamic, другие могут увидеть потенциал в этой новой фишки.