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

Автор оригинала: 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

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

Поделиться публикацией

Комментарии 29

    +10
    Вообще говоря, спорное нововведение. Мне совершенно не хочется, чтобы C# «оскриптился»…
      +2
      Соглашусь, из-за этого нововведения пропадает много вкусностей, начиная с IntelliSense и кончая тем, что в любой момент может выплюнутся такой runtime-exception, даже, если код успешно скомпилировался.
      Но, собтсвенно, никто и ничего не мешает его не использовать.
        0
        Вобщемто выбор есть всегда.
      +3
      Но ведь найдутся «мастера», которые будут пихать динамические объекты везде где вздумается.

      К тому же, как Вы правильно заметили, возникает опасность появления runtime-exception'ов. Это значит, что вызовы методов динамических объектов должны находится в блоке try-catch или каким-то образом декларировать это на уродвне описания метода. В отличие от Java, в C# используется более мягкий подход к обработке исключений. Компилятору безразлично, перехватили ли вы исключение или нет. Единственный способ указать, что метод может выбрасывать RuntimeBinderException — использовать xml-документацию, на которую всем (в том числе, компилятору) по большому счёту начхать. В конечном итоге, невнимание к обработке исключений «RuntimeBinderException» может завершиться падением приложения.
        0
        Промахнулся веткой =) Это был ответ на этот комментарий.
          +1
          Да, это, конечно, так.
          Но живут ведь люди применяя тот же Reflection — а там тоже опасность исключений велика. Главное все правильно продумать.
            0
            Reflection позиционируется как инструмент для профессионалов, он громоздкий и неудобен в использовании. Для его правильного и адекватного применения нужно обладать довольно серьёзным опытом разработки на C#.
            А dynamic-objects — это такой синтаксический сахар, который возлагает на себя все проблемы, связанные с reflection-ом. Мне кажется, что эта фича должна понравиться тем, кто решил начать изучать C# после того, как поигрался немного с JavaScript.
              +2
              Насколько я понял из статьи, в которой автор провел второе тестирование, dynamic — не просто сахар для Reflection, иначе он не получил бы таких кардинальных различий:
              Compile Time Bound: 6 ms
              Dynamically Bound with dynamic keyword: 45ms
              Dynamically Bound with MethodInfo.Invoke — 10943ms
              Dynamically Bound with DynamicMethod — 8ms

              MethodInfo.Invoke — это и есть Reflection
                0
                На сколько я понял, это всё-таки рефлекшн, но не через MethodInfo.Invoke, а с помощью генерации делегата, который вызывает нужный метод. Это работает быстрее, поскольку параметры делегата жёстко заданы и нет необходимости передавать их в виде массива, а потом матчить их в рантайме на параметры функции, как это происходит в случае MethodInfo.Invoke.
                Но как видно из приведённой Вами таблицы, ручная генерация делегатов с помощью класса DynamicMethod (который появился ещё в .NET 2.0) работает даже быстрее, чем динамические объекты в C# 4.0.
                  +3
                  Не только. typeof(TestClass).GetMethod("TestMethod1"); — это тоже Reflection.
                  Просто в случае MethodInfo.Invoke это чисто позднее связывание, в то время, как DynamicMethod — «комбинированный подход».

                  Если вам интересно, могу порекомендовать эту статью.
            0
            dynamic-ом не будут злоупотреблять из-за того, что с ним не заработает IntelliSense.

            Зато уж если есть необходимость работать с классом, который не достать на этапе компиляции — новый синтаксис в тысячу раз лучше, чем Reflection.

            А Exception вполне может выкинуть и сам вызываемый метод, что ж поделаешь.
          • НЛО прилетело и опубликовало эту надпись здесь
              +1
              Что-то не нравятся мне эти нововведения. На днях почитали «New features in C# 4.0», чувствую, прослыву ретроградом. Ничего действительно полезного для себя так и не нашел. Разве что Generic Covariance.
              Старый добрый DynamicMethod в пять раз уделал все «новинки».
                0
                я думаю это будет использоваться скорее как замена гиморной рефлексии
                Пример:
                dynamic object = GetDynamicObject(…);
                object.Method(7);
                  +2
                  So, how does C# 4.0's dynamic type work, exactly? Tune in. (по ссылке: видео-интервью с несколькими разработчиками С#.Парни увлеченно все объясняют возле доски.Думаю кому-нибудь будет интересно посмотреть)
                    +1
                    Исправьте, пожалуйста, первый абзац. Два раза одно и то же написано.
                      0
                      Спасибо, исправил.
                      +9
                      Народ… вы, мне кажется, не воткнули в прелести позднего инициирования. =))
                      Я больше чем уверен, что эта фишка разработана по ооочень многочисленным просьбам, и сам рад её появлению!
                      Вы когда-нибудь работали с Офисом (как самыыый часто встречаемый пример)? или со сторонними сервисами, через СОМ?
                      чтобы юзать такой сервис, надо было писать охрененную заглушку, чтобы взаимодействовать с ними! нужно было прописывать пути строго к тому, где лежат реализации (да хоть длл)? а как же версионность?
                      Написал ты прогу под 2000 офис, там одни длл с интерфейсными методами. пытаешь запустить её где стоит 2007… ФИГ! она не заработает!
                      А так… просто вызываешь у сервиса метод по имени и все! ты только привязан к сигнатуре прототипа и все =)

                      Так что поверьте «dynamic » — это тема!!!
                        +2
                        как мне нравятся минусующие тут люди… =))))
                        второй раз не выдерживаю и решаюсь что-нибудь написать… и снова минуса… причем оба раза невыдерживал как раз из-за совсем дилетантских ля=ля на тему =)))

                        но желание писать сейчас отпало ещё больше. Удачи =))
                          +1
                          Дело скорее всего в том, что многие люди (в том числе и я) считают ± как согласен/не согласен, а не хороший/плохой комментарий.
                          –1
                          Но зачем ради этого вводить в язык специальный тип? Это ведь бывает нужно очень редко!
                          Вполне можно было бы обойтись каким-нибудь самописным фреймворком. Вызовы можно оформить так:

                          class FooBar {
                          public void Method1(string param1, string param2) {...}
                          }

                          var x = new FooBar();
                          Dynamic o = new Dynamic(x);
                          o[«Method1»](param1, param2);

                          Работает помедленнее, но суть не меняется.
                            +1
                            от части ты прав, все и делали хитрые фраперы, но когда это нейтивно поддерживается, имхо все лучше
                            но все-таки этот механизм кросс модельный (если так можно сказать)

                            самый основной "+" — тебе не надо указывать явную реализацию того, что ты хочешь юзать на момент компиляции, не надо хитрых фратеров, не надо ручками ничего делать

                            все равно что: зачем foreach, если есть for?

                            позднее связывание уже доисторический механизм, и есть везде.

                            и никакого «оскрипчивания» не происходит… С# строготипизированный язык, и чтобы использовать позднее связывание надо или левой рукой чесать правое ухо, или дать возможность нейтивно его юзать.
                              +1
                              Все правильно. Самому приходилось работать с 1С 7.7, писал веб сервис один, который с ней работает. Так я этот сервис написал на VB, так как там это все есть и код получился в разы короче и интуитивно понятнее.
                                0
                                100% верно. Чем сложнее приложение тем больше там нужно позднего связывания, анонимных типов и прочих «фишек», а так как они сделаны от MS то они намного лутче чем бы ми их сами «на коленке» писали…

                                PS: об этом говорит опыт Smalltalk…
                                www.smalltalk.ru/articles/smalltalk.html
                                  0
                                  Вот расскажите, какая связь между сложностью приложения и, к примеру, анонимными типами?
                                    0
                                    Почитайте статью о Smalltalk, там отрожено мнение с которым я абсолютно солидарен :)
                                      0
                                      Не увидел там никакого мнения.

                                      Коли речь зашла о статьях, то прочитайте статью Брукса «No Silver Bullet — Essence and Accident in Software Engineering»: там как раз по поводу Essential и Accidental Complexity. Вот эти все «фишки», использованные «for the hell of it», вообще говоря, — привнесение Accidental Complexity.
                          +4
                          Хорошо когда оно есть, даже если оно тебе и не надо. Плохо когда надо — а нету
                            0
                            Когда этого «оно» становится слишком много
                            и оно тебе не надо — это плохо/некомфортно.
                            (Из-за чего я и отказался от менедж с++)

                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.