Студопедия

Главная страница Случайная страница

Разделы сайта

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






Динамическое связывание






Определение значения операции, основывающееся на типе или значении составных частей выражения (аргументов, операндов, получателей) в языке 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>






© 2023 :: MyLektsii.ru :: Мои Лекции
Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав.
Копирование текстов разрешено только с указанием индексируемой ссылки на источник.