Для чего используется тип dynamic

Ключевое слово dynamic

Ключевое слово var позволяет объявлять локальную переменную таким образом, что ее действительный тип данных определяется начальным присваиванием (это называется неявной типизацией). Как только начальное присваивание выполнено, вы получаете строго типизированную переменную, и любая попытка присвоить ей несовместимое значение приведет к ошибке компиляции.

Чтобы приступить к исследованию ключевого слова C# dynamic, создадим консольное приложение. После этого поместим в класс Program показанный ниже метод и удостоверимся, что финальный оператор кода действительно инициирует ошибку во время компиляции, если убрать с него символы комментария:

Для чего используется тип dynamic. Смотреть фото Для чего используется тип dynamic. Смотреть картинку Для чего используется тип dynamic. Картинка про Для чего используется тип dynamic. Фото Для чего используется тип dynamic

Динамическую переменную от переменной, объявленной неявно или через ссылку System.Object, значительно отличает то, что она не является строго типизированной. Другими словами, динамические данные не типизированы статически. Для компилятора C# это выглядит так, что элемент данных, объявленный с ключевым словом dynamic, может получить какое угодно начальное значение, и на протяжении времени его существования это значение может быть заменено новым (и возможно, не связанным с первоначальным).

Вызов членов на динамически объявленных данных

Теперь, учитывая, что тип данных dynamic может на лету принимать идентичность любого типа (как переменная типа System.Object), следующий вопрос, который наверняка возник, связан с вызовом членов на динамической переменной (свойств, методов, индексаторов, регистрации событий и т.п.). В отношении синтаксиса никаких отличий нет. Нужно просто применить операцию точки к динамической переменной, указать общедоступный член и передать ему необходимые аргументы.

Однако (и это очень важно) корректность указываемых членов компилятором не проверяется! Помните, что в отличие от переменной, объявленной как System.Object, динамические данные не являются статически типизированными. Вплоть до времени выполнения не известно, поддерживают ли вызываемые динамические данные указанный член, переданы ли корректные параметры, правильно ли указан член, и т.д. Поэтому, как бы странно это не выглядело, следующий метод cкомпилируется без ошибок:

Обратите внимание, что во втором вызове WriteLine() производится обращение к методу по имени toupper() на динамической переменной. Как видите, textData имеет тип string, и потому известно, что у этого типа нет метода с таким именем в нижнем регистре. Более того, тип string определенно не имеет метода по имени Foo(), который принимает int, string и DateTime!

Тем не менее, компилятор C# ни о каких ошибках не сообщает. Однако если вызвать этот метод в Main(), возникнет ошибка времени выполнения.

Другое значительное отличие между вызовом членов на динамических и строго типизированных данных состоит в том, что после применения операции точки к элементу динамических данных средство IntelliSense в Visual Studio 2010 не активизируется. Вместо этого отображается следующее общее сообщение:

Для чего используется тип dynamic. Смотреть фото Для чего используется тип dynamic. Смотреть картинку Для чего используется тип dynamic. Картинка про Для чего используется тип dynamic. Фото Для чего используется тип dynamic

То, что средство IntelliSense недоступно с динамическими данными, имеет смысл. Однако это означает, что при наборе кода C# с такими переменными следует соблюдать исключительную осторожность. Любая опечатка или некорректный регистр символов в имени члена приведет к ошибке времени выполнения, а именно — к генерации экземпляра класса RuntimeBinderException.

Сборка Microsoft.CSharp.dll

Для чего используется тип dynamic. Смотреть фото Для чего используется тип dynamic. Смотреть картинку Для чего используется тип dynamic. Картинка про Для чего используется тип dynamic. Фото Для чего используется тип dynamic

Как можно догадаться по их именам, оба класса представляют собой строго типизированные исключения. Более общий класс — RuntimeBinderException — представляет ошибку, которая будет сгенерирована при попытке вызова несуществующего члена на динамическом типе данных (как в случае методов toupper() и Foo()). Та же ошибка будет инициирована, если будут указаны неверные данные параметров для существующего члена.

Поскольку динамические данные столь изменчивы, каждый вызов члена на переменной, объявленной с ключевым словом dynamic, должен быть помещен в правильный блок try/catch, и предусмотрена соответствующая обработка ошибок.

Разумеется, процесс помещения всех динамических вызовов методов в блоки try/catch довольно утомителен. Если вы тщательно следите за написанием кода и передачей параметров, то это делать не обязательно. Однако перехват исключений удобен, когда заранее не известно, будет ли член представлен в целевом типе.

Источник

пятница, 27 января 2012 г.

Что такое dynamic, и с чем его едят.

Type of d is System.String
Type of d is System.Int32
Type of d is System.Boolean
Type of d is Sample.Employee

Результат был бы идентичным, если переменная d была бы объявлена как object. Но dynamic предоставляет ряд дополнительных возможностей, которых нет у System.Object.

Для вызова общедоступных членов у динамической переменной нужно просто применить операцию точки и указать интересующий нас метод (свойство, событие и т.д.) и передать аргументы (если необходимо). Как уже известно, объект типа dynamic обходит проверку статического типа в отличие от объекта типа System.Object. В связи с этим компилятор не может проверить корректность вызываемых методов (свойств, событий. ), поддерживает ли их объект неизвестно вплоть до прямого к нему обращения. Следующий код скомпилируется без ошибок, но при запуске возникнет ошибка времени выполнения RuntimeBindingException: Переменная d имеет тип string и мы без проблем получаем длину строки обращаясь к свойству Length. Но у типа string нету метода по имени Foo(), принимающего в качестве аргументов Boolean и int, да и метода Count() у типа string никогда не было. Запустив данный код мы получим следующее сообщение:

Unhandled Exception: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: “string” does not contain a definition for “Foo”

Поэтому используя динамические переменные нужно быть очень внимательным, вызывая их общедоступные члены. Любая ошибка в имени метода, свойства. приведет к RuntimeBinderException. Логично допустить, что вызов членов на динамической переменной желательно помещать в блок try/catch отлавливая RuntimeBinderException.

«Я есмь альфа и омега, начало и конец…»
(Библия: Откровение. 1:8)

Источник

Погружаемся в глубины C# dynamic

Одним из наиболее заметных дополнений в C# 4 является dynamic. Об этом рассказано много и не раз. Но всегда выпускается из виду DLR (Dynamic Language Runtime). В данной статье мы рассмотрим внутреннее устройство DLR, работу самого компилятора, а также дадим определение понятиям статически-, динамически- типизированный язык со слабой и сильной типизациями. И, конечно же, не останется без внимания техника PIC (Polymorphic Inline Cache), используемая, например, в Google V8 engine.

Перед тем как двигаться дальше, хотелось бы освежить некоторые термины и понятия.

Чтобы не повторятся, под выражением переменная мы будем подразумевать любой объект данных (переменная, константа, выражение).

Языки программирования по критерию проверки типов обычно делятся на статически типизированные (переменная связывается с типом в момент объявления и тип не может быть изменен позже) и динамически типизированные (переменная связывается с типом в момент присваивания значения и тип не может быть изменен позже).

C# является примером языка со статической типизацией, в то время как Python и Ruby — динамически.

По критерию политики безопасности типов выделяют языки со слабой (переменная не имеет строго определенный тип) и сильной/строгой (переменная имеет строго определенный тип, который не может быть изменен позже) типизацией.

C# 4 dynamic keyword

Хоть dynamic и добавляет возможность написания чистого кода и взаимодействия с динамическими языками вроде IronPython и IronRuby, C# не перестает быть статически типизированным языком с сильной типизацией.

Перед детальным рассмотрением механизма самого dynamic, приведем пример кода:

Результат выполнения представлен ниже на скриншоте:

Для чего используется тип dynamic. Смотреть фото Для чего используется тип dynamic. Смотреть картинку Для чего используется тип dynamic. Картинка про Для чего используется тип dynamic. Фото Для чего используется тип dynamic

И что же мы видим? Какая же здесь типизация?

Отвечу сразу: типизация сильная, и вот почему.

В отличие от других встроенных типов языка C# (например, string, int, object и т.п.), dynamic не имеет прямого сопоставления ни с одним из базовых типов BCL. Вместо этого, dynamic – специальный псевдоним для System.Object с дополнительными метаданными, необходимыми для правильного позднего связывания.

Будет преобразован к виду:

Как видно, объявляется переменная d типа object. Далее в дело вступают binders из состава библиотеки Microsoft.CSharp.

Для каждого динамического выражения в коде компилятор генерирует отдельный динамический узел вызов, представляющий собой саму операцию.

будет сгенерирован класс такого вида:

Типом поля <>p__Sitee является класс System.Runtime.CompilerServices.CallSite. Рассмотрим его поподробнее.

Хотя поле Target и является обобщенным, на самом деле всегда является делегатом. И последняя строка в вышеприведенном примере не просто вариация операции:

Статичный метод Create класса CallSite представляет собой:

Поле Target является L0-кэшем (существуют также и L1-, и L2-кэши), которое используется для быстрой диспетчеризации вызовов на основе истории вызовов.

Обращаю внимание, что узел вызова является «самообучающимся», поэтому DLR необходимо периодически обновлять значение Target.

Для описания логики работы DLR приведу ответ Эрика Липперта по этому поводу (вольный перевод):

Сначала среда выполнения решает с объектом какого типа мы имеем дело (COM, POCO).

Далее в дело вступает компилятор. Так как необходимость в лексере и парсере отсутствует, DLR использует специальную версию компилятора C#, имеющего только анализатор метаданных, семантический анализатор выражений, а также генератор кода, который вместо IL генерирует Expression Trees.

Анализатор метаданных использует рефлексию, чтобы установить тип объекта, который потом передается семантическому анализатору для установления возможности вызова метода или выполнения операции. Далее происходит построение Expression Tree, как если бы Вы использовали лямбда-выражение.

Компилятор C# возвращает обратно дерево выражений в DLR вместе с политикой кэширования. DLR потом сохраняет данный делегат в кэше, ассоциирующимся с узлом вызовов.

Для этого используется свойство Update класса CallSite. При вызове динамической операции, хранящейся в поле Target происходит перенаправление к свойству Update, где и происходит вызов байндеров. Когда в следующий раз произойдет вызов, вместо повторного выполнения вышеперечисленных действий, будет использоваться уже готовый делегат.

Polymorphic Inline Cache

Производительность динамических языков страдает из-за дополнительных проверок и поисковых запросов, выполняющихся во всех местах вызова. Прямолинейная реализация постоянно ищет члены в списках приоритета класса и, возможно, разрешает перегрузку типов аргументов метода каждый раз, когда исполняется строчка кода. В языках со статической типизацией (или с достаточным количеством указаний типов в коде и выводом типов) можно генерировать инструкции или вызовы функций среды исполнения, которые подходят для всех точек вызова. Это возможно, потому что статические типы позволяют узнать все, что нужно, во время компиляции.

На практике повторные операции над одними и теми же типами объектов можно привести к общему типу. Например, при первом вычислении значения выражения x + y для целых x и y можно запомнить фрагмент кода или точную функцию времени исполнения, которая складывает два целых числа. Тогда при всех последующих вычислениях значения этого выражения искать функцию или фрагмент кода уже не потребуется благодаря кэшу.

Вышеприведенный механизм кэширования делегатов (в данном случае), когда узел вызова является самообучающимся и обновляющимся называется Polymorphic Inline Cache. Почему?

Polymorphic. Target узла вызова может иметь несколько форм, исходя из типов объектов, используемых в динамической операции.

Inline. Жизненный цикл экземпляра класса CallSite проходит именно в месте самого вызова.

Cache. Работа основана на различных уровнях кэша (L0, L1, L3).

Источник

Dynamic в C#: рецепты использования

Это заключительная часть цикла про Dynamic Language Runtime. Предыдущие статьи:

Для чего используется тип dynamic. Смотреть фото Для чего используется тип dynamic. Смотреть картинку Для чего используется тип dynamic. Картинка про Для чего используется тип dynamic. Фото Для чего используется тип dynamic

Когда без dynamic не обойтись

Таких случаев нет. Всегда можно написать аналогичный по функционалу код в статическом стиле, разница лишь в удобстве чтения и объеме кода. Например, при работе с COM-объектами вместо dynamic можно использовать рефлексию.

Когда dynamic полезен

Работа с COM-объектами

В первую очередь это, конечно же, работа с COM-объектами, ради которой всё это и затевалось. Сравните код, получившийся при помощи dynamic и при помощи рефлексии:

Как правило, для работы с COM-объектами через рефлексию приходится создавать развесистые классы с обертками под каждый метод/свойство. Есть и менее очевидные плюшки типа возможности не заполнять ненужные вам параметры (обязательные с точки зрения COM-объекта) при вызове метода через dynamic.

Работа с конфигами

Ещё один хрестоматийный пример — работа с конфигами, например с XML. Без dynamic:

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

Но, согласитесь, это выглядит гораздо менее изящно, чем через dynamic.

Работа с внешними ресурсами

Предыдущий пункт можно обобщить на любые действия с внешними ресурсами. У нас всегда есть две альтернативы: использование dynamic для получения кода в нативном C# стиле либо статическая типизация с «магическими строками». Давайте рассмотрим пример с REST API запросом. С dynamic можно написать так:

Где наш динамический класс по запросу свойства отправит запрос вида

Затем десериализует его и вернем нам уже готовый к целевому использованию массив кошек. Без dynamic это будет выглядеть примерно так:

Замена рефлексии

В предыдущем примере у вас мог возникнуть вопрос: почему в одном случае мы десериализуем возвращаемое значение к конкретному типу, а в другом — нет? Дело в том, что в статической типизации нам нужно явно привести объекты к типу Cat для работы с ними. В случае же dynamic, достаточно десериализовать JSON в массив объектов внутри нашего динамического класса и вернуть из него object[], поскольку dynamic берёт на себя работу с рефлексией. Приведу два примера того, как это работает:

Тот же самый принцип, что и при работе с COM-объектами.

Visitor

С помощью dynamic можно очень элегантно реализовать этот паттерн. Вместо тысячи слов:

Теперь при передаче объекта типа Sword в метод DoSomeWork будет вызван метод InternalDoSomeWork(Sword item).

Выводы

Плюсы использования dynamic:

Заключение

На мой взгляд, наибольший профит от использования dynamic разработчик получит в следующих ситуациях:

Если вы работаете с COM-объектами или доменами в сервисах/продуктах, подразумевающих длительное непрерывное время работы — лучше dynamic не использовать, несмотря на то, что именно для таких случаев он и создавался. Даже если вы досконально знаете что и как делать и никогда не допускаете ошибок, рано или поздно может прийти новый разработчик, который этого не знает. Итогом, скорее всего, будет трудновычислимая утечка памяти.

Источник

Когда и зачем применяется динамический тип

Зачем используют тип dynamic и как понять, когда его использовать. Тем более что в теории написано, что правильно ли мы все сделали с этим типом, будет видно только в рантайме. Вижу простые примеры, которые ничего не показывают именно зачем он и когда его надо применять. Вот пример теста от Luxoft. Зачем ту. динамический тип.

На выходе получается

Для чего используется тип dynamic. Смотреть фото Для чего используется тип dynamic. Смотреть картинку Для чего используется тип dynamic. Картинка про Для чего используется тип dynamic. Фото Для чего используется тип dynamic

1 ответ 1

Допустим нам нужно реализовать класс, который возвращает какой-то объект, который представляет из себя строчку результируюзего запроса к БД.

Нам заранее никак не известно, какой запрос отправит пользователь в БД, что бы определить поля класса, мы не знаем какие типы на вернет SQL.

Что мы можем сделать?

Он позволяет в естественной манере(через оператор «точка») общаться с неизвестными на этапе компиляции членами типа(полями, методами и т п. ), как будто они есть.

Без него, нам бы пришлось в явном виде пользоваться рефлексией( dynamic прячет ее внутри себя).

Более того, если мы унаследуемся от DynamicObject и переопределим методы, то сможем создать свою оболочку с блэекджеком и всем остальным.

За все приходится платить=>есть конечно и минусы:

Как следствие, если такое возможно, то его нужно избегать и использовать только, когда в нем есть необходимость.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *