Pull to refresh

Новые возможности C# 4.0. Часть 1: dynamic

Reading time3 min
Views5.9K
Original author: Justin Etheredge
Одна из самых интересных возможностей язык C# 4.0, который был представлен на PDC является новое ключевое слово — dynamic. Оно позволяет разработчику объявить объект, привязка к методам которого, будет осуществлятся на этапе выполнения, а не компиляции. Примечательно, что класс, который инстанциирует этот объект объявляется стандартным способом.
  1. public class TestClass
  2. {
  3.   public void TestMethod1()
  4.   {
  5.     Console.WriteLine("test method 1");
  6.   }
  7.  
  8.   public void TestMethod2()
  9.   {
  10.     Console.WriteLine("test method 2");
  11.   }    
  12. }
* This source code was highlighted with Source Code Highlighter.
Теперь мы можем вызывать его методы как обычно:
  1. var test = new TestClass();
  2. test.TestMethod1();
  3. test.TestMethod2();
* This source code was highlighted with Source Code Highlighter.
Как мы и ожидали все прекрасно скомпилировалось. Теперь воспользуемся новым ключевым словом dynamic:
  1. dynamic test = new TestClass();
  2. test.TestMethod1();
  3. test.TestMethod2();
* This source code was highlighted with Source Code Highlighter.
Ничего не изменилось? Отнюдь. Все точно так же компилируется, как и раньше, но теперь вызовы методов будут привязываться на этапе выполнения, а не компиляции. Тоесть, если мы напишем вот так:
  1. dynamic test = new TestClass();
  2. test.TestMethod1();
  3. test.TestMethod2();
  4. test.TestMethod3();
* This source code was highlighted with Source Code Highlighter.
Код все-равно успешно скомпилируется, но после запуска мы получим данное исключение:
Что же тут произошло? Вот что нам подсказал Reflector:
  1. private static void Main(string[] args)
  2.   {
  3.     object test = new TestClass();
  4.     if (<Main>o__SiteContainer0.<>p__Site1 == null)
  5.     {
  6.       <Main>o__SiteContainer0.<>p__Site1 = CallSite<Action<CallSite, object>>.Create(new CSharpCallPayload(Microsoft.CSharp.RuntimeBinder.RuntimeBinder.GetInstance(), false, false, "TestMethod1", typeof(object), null));
  7.     }
  8.     <Main>o__SiteContainer0.<>p__Site1.Target(<Main>o__SiteContainer0.<>p__Site1, test);
  9.     if (<Main>o__SiteContainer0.<>p__Site2 == null)
  10.     {
  11.       <Main>o__SiteContainer0.<>p__Site2 = CallSite<Action<CallSite, object>>.Create(new CSharpCallPayload(Microsoft.CSharp.RuntimeBinder.RuntimeBinder.GetInstance(), false, false, "TestMethod2", typeof(object), null));
  12.     }
  13.     <Main>o__SiteContainer0.<>p__Site2.Target(<Main>o__SiteContainer0.<>p__Site2, test);
  14.   }
* This source code was highlighted with Source Code Highlighter.
Для начала компилятор сгенерировал поле o__SiteContainer0, для хранения информации о месте вызова. Если вы посмотрите на переменную test, которая содержит наш объект, то увидите, что ее тип стал типом object. Так что, в реальности, это не динамический тип, а всего-лишь помощник со стороны компилятора.Дальше идет проверка мест вызова(callsite) на null и если они ему равны, то используется CallSite.Create, в который передается объект CSharpCallPayload, который содержит всю информацию о методе, который мы вызываем. Как только место вызова указано, то оно просто вызывается у нашей переменной test. Как видите — компилятор все сделал за нас.

В данном примере то что мы делаем кажется чертовски бесполезным, но мощь этой возможности проявляется тогда, когда мы используем тип, методы которого мы не знаем на этапе компиляции. Это может прийти от динамических языков(например IronRuby), если это сгенерированный класс, или что угодно из того, для чего мы используем Reflection.

Ремарка: я принял рещение исключить из перевода часть про тестирование производительности, так как в статье C# 4.0 New Features Part 1.1 — dynamic keyword second look автор провел новое тестирование и оно показало заметно лучшие результаты.

Перевод статьи C# 4.0 New Features Part 1 — dynamic keyword

Кросспост из моего блога

Tags:
Hubs:
Total votes 43: ↑39 and ↓4+35
Comments29

Articles