Как известно, в .NET 3.0, C# обзавелся ключевым словом var, которое позволяет объявить локальную, неявно типизированную переменную.
var s = "String"; //"s" имеет тип string
Также переменную можно объявить как object. Ни для кого не секрет, что System.Object возглавляет иерархию классов .NET Framework и переменная объявленная как object, может представлять все что угодно. Таким образом мы получим следующее:
object o = new Employee() {Name = "Employee1"};
//"o" может быть приведена к типу Employee
Console.WriteLine("Employee’s name is: {0}", ((Employee)o).Name);
В .NET 4.0, С# приобрел ключевое слово dynamic. Для его использования в проекте необходимо иметь ссылку на пространство имен Microsoft.CSharp.RuntimeBinder.Dynamic чем-то похож на object. Типу dynamic может быть присвоено любое значение. Наличие трех способов объявления переменных, внутренний тип которых не указан явно, может смутить. Но в отличие от переменной объявленной неявно с помощью var и от переменной объявленной через ссылку с помощью object, динамическая переменная объявленная с помощью dynamic не является строго типизированной. Т.е. переменная объявленная с ключевым словом dynamic может получить какое угодно значение и за все время жизни этой переменной, ее значение может быть заменено, и необязательно новое содержание должно иметь тип связанный с первоначальным значением:
dynamic d = "String";
Console.WriteLine("Type of d is {0}", d.GetType());
d = 15;
Console.WriteLine("Type of d is {0}", d.GetType());
d = true;
Console.WriteLine("Type of d is {0}", d.GetType());
d = new Employee() {Name = "Employee1"};
Console.WriteLine("Type of d is {0}", d.GetType());
Вывод на экран будет следующим:
Type of d is System.String
Type of d is System.Int32
Type of d is System.Boolean
Type of d is Sample.Employee
Для вызова общедоступных членов у динамической переменной нужно просто применить операцию точки и указать интересующий нас метод (свойство, событие и т.д.) и передать аргументы (если необходимо). Как уже известно, объект типа dynamic обходит проверку статического типа в отличие от объекта типа System.Object. В связи с этим компилятор не может проверить корректность вызываемых методов (свойств, событий...), поддерживает ли их объект неизвестно вплоть до прямого к нему обращения. Следующий код скомпилируется без ошибок, но при запуске возникнет ошибка времени выполнения RuntimeBindingException:
dynamic d = "some data";
Console.WriteLine(d.Length);
d.Foo(false, 10);
d.Count();
Переменная 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)
Dynamic может выступать в роли: - типа поля - типа свойства - типа возвращаемого методом значения - типа аргументов, передаваемых методу - типа локальной переменной
public class ClassWithDynamicMembers
{
private dynamic dynamicField;
public dynamic DynamicProperty { get; set; }
public dynamic StrangeDynamicMethod(dynamic dynamicParam1,
dynamic dynamicParam2)
{
dynamic dynamicLocalVar = "dynamicVar";
if (dynamicParam1 is string)
dynamicLocalVar += dynamicParam1;
if (dynamicParam2 is Boolean)
return dynamicParam2;
return false;
}
}
Но есть одно неприятное ограничение: объект типа dynamic не воспринимает расширяющие методы и как следствие с таким объектом не получится использовать технологию LINQ:
dynamic d = new List() {"a", "b", "c"};
var linqData = from data in d sеlect data;
Применение ключевого слова dynamicВ связи с тем, что во время компиляции предполагается, что объекты с типом dynamic могут поддерживать любые операции, то таким объектам абсолютно все-равно откуда они получат свое значение – из COM, из динамического языка типа IronPython или IronRuby, из объектной модели DOM HTML, посредством рефлексии или из какого-либо другого места программы.
Работа с динамическими языками такими, как IronPython и IronRuby осуществляется благодаря среде DLR (Dynamic Language Runtime), появившейся в .NET 4.0, которая собственно и обеспечивает инфраструктуру, поддерживающую тип dynamic в C#. Описание среды DLR, ее архитектуры и прочего не является целью данной статьи. Но хотелось бы остановиться на dynamic и его упрощенном взаимодействии с API COM.
Взаимодействие с API COM
В прошлых версиях .NET, при разработке приложений на C#, имеющих дело с библиотеками COM, возникал ряд сложностей. Часто COM-библиотеки определяют метода в которых присутствуют необязательные параметры, что требовало указания значения Type.Missing. Сейчас, если необязательные параметры не указаны, то они автоматически получат значение Type.Missing во время компиляции. Еще одним примером проблематичной работы с COM-библиотеками, можно назвать то, что многие методы COM могут принимать в качестве аргументов и возвращать тип данных Variant. Также как и dynamic, объект типа Variant может принимать значения разных типов данных на лету. До .NET 4.0 и появления ключевого слова dynamic в С#, работать с данными типа Variant можно было посредством object, что приводило к многочисленным приведениям типов. С использованием dynamic проблемы в работе с типами Variant как рукой сняло.
Например, для создания пустого Word файла, сохранения его с определенным именем и последующим закрытием Word'а, без использования dynamic, нужно совершить следующие телодвижения:
Word.Application wordApp = new Word.Application() { Visible = true };
object tm = Type.Missing;
object visibility = true;
object fileName = @"C:\COMPreDynamic.doc";
Word.Document word = wordApp.Documents.Add(ref tm, ref tm, ref tm,
ref visibility);
word.SaveAs(fileName);
word.Close(ref tm, ref tm, ref tm);
wordApp.Application.Quit(ref tm, ref tm, ref tm);
При работе с COM, dynamic используется неявно. Все что нужно сделать, это в подключенной COM библиотеке установить свойство Embed Interop Types в True. С этого момента все типы Variant начнут автоматически отображаться на динамические данные. Итак, с учетом вышесказанного, код с dynamic будет выглядеть намного читабельнее и проще:
Word.Application wordApp = new Word.Application() { Visible = true };
Word.Document word = wordApp.Documents.Add(Visible: true);
word.SaveAs(FileName: @"C:\COMWithDynamic.doc");
word.Close();
wordApp.Application.Quit();
Несколько слов в завершениеПольза от динамических типов несомненно есть (как минимум упрощенное взаимодействие с COM). Но даже если применять динамические типы в повседневных задачах (что без особых на то причин, крайне не рекомендуется), то важно помнить, что использование динамических типов приводит к потере безопасности типов и как результат код становится подвержен большему количеству ошибок RuntimeBinderException.
Комментариев нет:
Отправить комментарий