Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Базовые технологии платформы .NET.docx
Скачиваний:
15
Добавлен:
02.11.2018
Размер:
626.37 Кб
Скачать

2.24. Динамические типы

Динамические типы – новый элемент языка C# и платформы .NET четвёртой версии. При использовании динамического типа компилятор не выполняет у объекта проверку элементов типа. Такая проверка происходит при выполнении кода, после связывания динамической переменной с конкретным объектом.

Мотивом введения динамических типов была необходимость упростить работу C#-кода с COM-объектами и сценарными языками. Ниже приведён листинг, в котором выполняется взаимодействие с Microsoft Excel.

// получаем тип, соответствующий объекту Microsoft Excel

Type xlAppType = Type.GetTypeFromProgID("Excel.Application");

// объявляем переменную, используя динамический тип

dynamic xl = Activator.CreateInstance(xlAppType);

// вызываем некоторые методы Excel

xl.Visible = true; // показываем окно Excel

dynamic workbooks = xl.Workbooks; // создаем новый лист Excel

workbooks.Add(-4167);

xl.Cells[1, 1].Value2 = "C# Rocks!"; // заполняем ячейку листа

Пример показывает, что объект динамического типа объявляется при помощи ключевого слова dynamic. У такого объекта можно записать вызов любого метода или свойства, это не влияет на компиляцию. С точки зрения компилятора dynamic является эквивалентом object. Задача компилятора – «упаковать» информацию о действиях, производимых с динамическим объектом, чтобы среда исполнения могла правильно таким объектом распорядиться.

// объявляем простой класс

public class Foo

{

public void Do(string s) { Console.WriteLine(s); }

}

// код компилируется (!),

// но при выполнении 3-я строка генерирует исключение

dynamic obj = new Foo();

obj.Do("Hello");

obj.Prop = 3; // исключение!

Действия среды исполнения зависят от вида объекта, связываемого с динамической переменной:

  • Обычные объекты .NET – элементы типа определяются при помощи механизма отражения, работа с элементами происходит при помощи позднего связывания.

  • Объект, реализующий интерфейс IDynamicMetaObjectProvider – сам объект запрашивается о том, содержит ли он заданный элемент. В случае успеха работа с элементом делегируется объекту.

  • COM-объекты – работа с элементами происходит через интерфейс IDispatch.

Интерфейс IDynamicMetaObjectProvider позволяет разработчикам создавать типы, обладающие динамическим поведением. Обычно данный интерфейс не реализуется напрямую, а выполняется наследование от класса DynamicObject (интерфейс и класс находятся в пространстве имён System.Dynamic). Ниже приведён пример класса, унаследованного от DynamicObject.

public class MyDynamicType : DynamicObject

{

public override bool TryInvokeMember(InvokeMemberBinder binder,

object[] args, out object result)

{

Console.WriteLine("Вызов {0}.{1}()",

GetType(), binder.Name);

result = null;

return true;

}

public override bool TrySetMember(SetMemberBinder binder,

object value)

{

Console.WriteLine("Установка {0}.{1} в значение {2}",

GetType(), binder.Name, value);

return true;

}

public void DoDefaultWork()

{

Console.WriteLine("Некое действие");

}

}

// пример использования

dynamic d = new MyDynamicType();

d.DoDefaultWork(); // "Некое действие"

d.DoWork(); // "Вызов MyDynamicType.DoWork()"

d.Value = 42; // "Установка MyDynamicType.Value в значение 42"

d.Count = 12; // "Установка MyDynamicType.Count в значение 12"

Класс System.Dynamic.ExpandoObject позволяет при выполнении программы добавлять и удалять элементы своего экземпляра:

public sealed class ExpandoObject : IDynamicMetaObjectProvider,

IDictionary<string, object>,

INotifyPropertyChanged

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

dynamic sample = new ExpandoObject();

sample.Caption = "Свойство"; // добавляем свойство Caption

sample.Number = 10; // и числовое свойство Number

sample.Increment = (Action)(() => { sample.Number++; });

// работаем с объектом sample

Console.WriteLine(sample.Caption); // Свойство

Console.WriteLine(sample.Caption.GetType()); // System.String

sample.Increment();

Console.WriteLine(sample.Number); // 11

Объект ExpandoObject явно реализует IDictionary<string, object>. Это позволяет инспектировать элементы объекта при выполнении программы. Также при помощи словаря удаляются элементы объекта ExpandoObject.

dynamic employee = new ExpandoObject();

employee.Name = "John Smith";

employee.Age = 33;

foreach (var property in (IDictionary<string, object>)employee)

{

Console.WriteLine(property.Key + ": " + property.Value);

}

((IDictionary<string, object>)employee).Remove("Name");

Реализация в ExpandoObject интерфейса INotifyPropertyChanged позволяет получать уведомления при изменении свойств объекта ExpandoObject.