![]() Главная страница Случайная страница КАТЕГОРИИ: АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника |
Динамическое связывание
Определение значения операции, основывающееся на типе или значении составных частей выражения (аргументов, операндов, получателей) в языке 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>
|