- •А.А. Волосевич
- •Содержание
- •1. Работа с числами
- •2. Представление даты и времени
- •3. Работа со строками и текстом
- •4. Преобразование информации
- •5. Сравнение для выяснения равенства
- •6. Сравнение для выяснения порядка
- •7. Жизненный цикл объектов
- •7.1. Алгоритм сборки мусора
- •7.2. Финализаторы и интерфейс iDisposable
- •7.3. Слабые ссылки
- •8. Перечислители и итераторы
- •9. Стандартные интерфейсы коллекций
- •10. Массивы и класс System.Array
- •11. Типы для работы с коллекциями-списками
- •12. Типы для работы с коллекциями-множествами
- •13. Типы для работы с коллекциями-словарями
- •14. Типы для создания пользовательских коллекций
- •15. Технология linq to Objects
- •1. Оператор условия Where().
- •2. Операторы проекций.
- •3. Операторы упорядочивания.
- •4. Оператор группировки GroupBy().
- •5. Операторы соединения.
- •6. Операторы работы с множествами.
- •7. Операторы агрегирования.
- •8. Операторы генерирования.
- •9. Операторы кванторов и сравнения.
- •10. Операторы разбиения.
- •11. Операторы элемента.
- •12. Операторы преобразования.
- •16. Работа с объектами файловой системы
- •17. Ввод и вывод информации
- •17.1. Потоки данных и декораторы потоков
- •2. Классы для работы с потоками, связанными с хранилищами.
- •3. Декораторы потоков.
- •4. Адаптеры потоков.
- •17.2. Адаптеры потоков
- •18. Основы xml
- •19. Технология linq to xml
- •20. Дополнительные возможности обработки xml
- •21. Сериализация времени выполнения
- •22. Контракты данных и xml-сериализация
- •23. Состав и взаимодействие сборок
- •24. Метаданные и получение информации о типах
- •25. Позднее связывание и кодогенерация
- •26. Атрибуты
- •27. Динамическое связывание
- •28. Файлы конфигурации
- •29. Диагностика и мониторинг
- •30. Процессы и домены
- •31. Основы многопоточного программирования
- •32. Синхронизация потоков
- •32.1. Критические секции
- •32.2. Синхронизация на основе подачи сигналов
- •32.3. Неблокирующие средства синхронизации
- •32.4. Разделение данных между потоками
- •33. Библиотека параллельных задач
- •33.1. Параллелизм на уровне задач
- •33.2. Параллелизм при императивной обработке данных
- •33.3. Параллелизм при декларативной обработке данных
- •33.4. Обработка исключений и отмена выполнения задач
- •33.5. Коллекции, поддерживающие параллелизм
- •34. Асинхронный вызов методов
- •Литература
27. Динамическое связывание
Определение значения операции, основывающееся на типе или значении составных частей выражения (аргументов, операндов, получателей) в языке C# обычно происходит на этапе компиляции и именуется статическим связыванием (static binding). Если этот процесс осуществляется при выполнении программы, то он называется динамическим связыванием (dynamic binding). Язык C# поддерживает динамическое связывание, начиная с четвёртой версии. Цель динамического связывания – позволить программам на C# взаимодействовать с динамическими объектами, то есть с объектами, которые не подчиняются обычным правилам системы типов C#. Примерами таких объектов являются:
– объекты динамических языков (IronPython, IronRuby, Jscript);
– «расширяемые» объекты, которые позволяют добавлять новые свойства во время выполнения программы (Internet Explorer DOM);
– объекты COM (например, в объектной модели Microsoft Office).
Для указания на необходимость динамического связывания в языке C# используется особый тип – dynamic. У объекта такого типа можно записать вызов любого метода или свойства, это не влияет на компиляцию.
public class Foo
{
public void Print(string s)
{
Console.WriteLine(s);
}
}
// этот код компилируется,
// но при выполнении третья строка генерирует исключение
dynamic obj = new Foo();
obj.Print("Hello world");
obj.Property = 10; // RuntimeBinderException
Тип dynamic может использоваться при объявлении локальной переменной, формального параметра или возвращаемого значения метода, элемента типа (поля, свойства), сконструированного универсального шаблона. Определено неявное преобразование dynamic и других типов друг в друга.
// метод с dynamic-параметрами и возвращаемым значением
public dynamic Div(dynamic x, dynamic y)
{
return x/y;
}
// неявные преобразования
int i = 7;
dynamic d = i;
int j = d;
В качестве примера использования dynamic рассмотрим класс, реализующий перечисляемый слаботипизированный кортеж:
public class IterableTuple : IEnumerable
{
private readonly List<dynamic> _storage;
public IterableTuple(params dynamic[] args)
{
_storage = new List<dynamic>(args);
}
public static IterableTuple Create(params dynamic[] args)
{
return new IterableTuple(args);
}
public dynamic this[int i]
{
get
{
return _storage[i];
}
}
public IEnumerator GetEnumerator()
{
return _storage.GetEnumerator();
}
}
// пример использования IterableTuple
var tuple = IterableTuple.Create(1.5, 12, "string", 69);
foreach (var item in tuple)
{
Console.WriteLine(item);
}
Компилятор заменяет объявление dynamic на объявление object, поэтому с точки зрения CLR эти типы эквиваленты. Работу с dynamic-объектом компилятор организует, используя класс Microsoft.CSharp.RuntimeBinder.Binder (сборка Microsoft.CSharp.dll). Способ нахождения элементов динамического объекта зависит от его конкретного типа:
1. Обычные объекты .NET – элементы определяются при помощи механизма отражения, работа с элементами происходит при помощи позднего связывания.
2. Объект, реализующий интерфейс IDynamicMetaObjectProvider – сам объект запрашивается о том, содержит ли он заданный элемент. В случае успеха работа с элементом делегируется объекту.
3. COM-объекты – работа происходит через интерфейс IDispatch.
Интерфейс IDynamicMetaObjectProvider позволяет разработчикам создавать типы, обладающие динамическим поведением. Обычно данный интерфейс не реализуется напрямую, а выполняется наследование от класса DynamicObject (интерфейс и класс находятся в пространстве имён System.Dynamic). В качестве примера использования DynamicObject приведём класс, обеспечивающий динамический доступ к атрибутам XML-элемента:
public static class XExtensions
{
public static dynamic DynamicAttributes(this XElement e)
{
return new XWrapper(e);
}
private class XWrapper : DynamicObject
{
private readonly XElement _element;
public XWrapper(XElement e)
{
_element = e;
}
// метод вызывается при попытке прочитать значение свойства
public override bool TryGetMember(GetMemberBinder binder,
out object result)
{
result = _element.Attribute(binder.Name).Value;
return true;
}
// метод вызывается при попытке установить значение свойства
public override bool TrySetMember(SetMemberBinder binder,
object value)
{
_element.SetAttributeValue(binder.Name, value);
return true;
}
}
}
// пример работы с XWrapper
XElement x = XElement.Parse(@"<Label Text=""Hello"" Id=""5""/>");
dynamic da = x.DynamicAttributes();
Console.WriteLine(da.Id); // 5
da.Text = "Foo";
Console.WriteLine(x.ToString()); // <Label Text="Foo" Id="5" />
Класс System.Dynamic.ExpandoObject позволяет при выполнении программы добавлять и удалять элементы своего экземпляра:
public sealed class ExpandoObject : IDynamicMetaObjectProvider,
IDictionary<string, object>,
INotifyPropertyChanged
Благодаря динамической типизации работа с пользовательскими элементами ExpandoObject происходит как работа с обычными элементами объекта. Ниже приведён пример расширения ExpandoObject двумя свойствами и методом (в виде делегата).
dynamic sample = new ExpandoObject();
sample.Caption = "The caption"; // добавляем свойство Caption
sample.Number = 10; // и числовое свойство Number
sample.Increment = (Action) (() => { sample.Number++; });
// работаем с объектом sample
Console.WriteLine(sample.Caption); // The caption
Console.WriteLine(sample.Caption.GetType()); // System.String
sample.Increment();
Console.WriteLine(sample.Number); // 11
Объект ExpandoObject явно реализует IDictionary<string, object>. Это позволяет инспектировать элементы объекта при выполнении программы и при необходимости удалять их.
// этот метод преобразует ExpandoObject в XElement
public static XElement ExpandoToXml(dynamic node, string nodeName)
{
var xmlNode = new XElement(nodeName);
foreach (var property in (IDictionary<string, object>) node)
{
if (property.Value is ExpandoObject)
{
xmlNode.Add(ExpandoToXml(property.Value, property.Key));
}
else if (property.Value.GetType() == typeof (List<dynamic>))
{
foreach (dynamic el in (List<dynamic>) property.Value)
{
xmlNode.Add(ExpandoToXml(el, property.Key));
}
}
else
{
xmlNode.Add(new XElement(property.Key, property.Value));
}
}
return xmlNode;
}
// пример работы с ExpandoToXml()
dynamic contact = new ExpandoObject();
contact.Name = "Patrick Hines";
contact.Phone = "206-555-0144";
contact.Address = new ExpandoObject();
contact.Address.Street = "123 Main St";
contact.Address.City = "Mercer Island";
contact.Address.State = "WA";
contact.Address.Postal = "68402";
dynamic res = ExpandoToXml(contact, "Contact");
Console.WriteLine(res);
// будет выведен следующий XML-фрагмент
// <Contact>
// <Name>Patrick Hines</Name>
// <Phone>206-555-0144</Phone>
// <Address>
// <Street>123 Main St</Street>
// <City>Mercer Island</City>
// <State>WA</State>
// <Postal>68402</Postal>
// </Address>
// </Contact>