Различия между ExpandoObject, DynamicObject и динамическим - PullRequest
159 голосов
/ 25 августа 2010

В чем различия между System.Dynamic.ExpandoObject, System.Dynamic.DynamicObject и dynamic?

В каких ситуациях вы используете эти типы?

Ответы [ 4 ]

139 голосов
/ 25 августа 2010

Ключевое слово dynamic используется для объявления переменных, которые должны иметь позднюю привязку.
Если вы хотите использовать позднюю привязку, для любого реального или воображаемого типа вы используете ключевое слово dynamic, а компилятор выполняетrest.

Когда вы используете ключевое слово dynamic для взаимодействия с обычным экземпляром, DLR выполняет вызовы с поздней привязкой к обычным методам экземпляра.

The IDynamicMetaObjectProvider interface позволяет классу контролировать свое поведение с поздним связыванием.
Когда вы используете ключевое слово dynamic для взаимодействия с реализацией IDynamicMetaObjectProvider, DLR вызывает методы IDynamicMetaObjectProviderи сам объект решает, что делать.

Классы ExpandoObject и DynamicObject являются реализациями IDynamicMetaObjectProvider.

ExpandoObject - это простой класс, который позволяет добавлять членык экземпляру и использовать их dynamic союзник.
DynamicObject - более продвинутая реализация, которая может быть унаследована для простого обеспечения настраиваемого поведения.

50 голосов
/ 21 июня 2017

Я постараюсь дать более четкий ответ на этот вопрос, чтобы четко объяснить, в чем различия между динамическими ExpandoObject и DynamicObject.

Очень быстро, dynamic - это ключевое слово. Это не тип как таковой. Это ключевое слово, которое указывает компилятору игнорировать статическую проверку типов во время разработки и вместо этого использовать позднюю привязку во время выполнения. Поэтому мы не будем тратить много времени на dynamic в остальной части этого ответа.

ExpandoObject и DynamicObject действительно являются типами. На поверхности они выглядят очень похожими друг на друга. Оба класса реализуют IDynamicMetaObjectProvider. Однако, копайте глубже, и вы обнаружите, что они совсем не похожи.

DynamicObject - это частичная реализация IDynamicMetaObjectProvider, предназначенная исключительно для того, чтобы служить отправной точкой для разработчиков для реализации своих собственных пользовательских типов, поддерживающих динамическую диспетчеризацию с настраиваемым базовым поведением хранения и извлечения для обеспечения работы динамической диспетчеризации.

  1. DynamicObject не может быть создан напрямую.
  2. Вы ДОЛЖНЫ расширить DynamicObject, чтобы он как-либо использовался вами как разработчиком.
  3. Когда вы расширяете DynamicObject, вы теперь можете предоставить ПОЛЬЗОВАТЕЛЬСКОЕ поведение, определяющее, как вы хотите, чтобы динамическая диспетчеризация разрешала данные, хранящиеся внутри вашего базового представления данных во время выполнения.
  4. ExpandoObject хранит базовые данные в словаре и т. Д. Если вы реализуете DynamicObject, вы можете хранить данные где угодно и как угодно. (например, как вы получаете и устанавливаете данные об отправке, полностью зависит от вас).

Короче говоря, используйте DynamicObject, когда вы хотите создать свои СОБСТВЕННЫЕ типы, которые можно использовать с DLR, и работать с любыми режимами CUSTOM, которые вы хотите.

Пример. Представьте, что вы хотите иметь динамический тип, который возвращает пользовательское значение по умолчанию при каждой попытке получить элемент, который НЕ существует (то есть не был добавлен во время выполнения). И по умолчанию будет сказано: «Извините, в этой банке нет файлов cookie!». Если вам нужен динамический объект, который ведет себя так, вам нужно контролировать, что происходит, когда поле не найдено. ExpandoObject не позволит вам сделать это. Поэтому вам нужно создать свой собственный тип с уникальным динамическим разрешением (диспетчеризацией) поведения элемента и использовать его вместо готового ExpandoObject.

Вы можете создать тип следующим образом: (Обратите внимание, приведенный ниже код приведен только для иллюстрации и может не работать. Чтобы узнать о том, как правильно использовать DynamicObject, есть много статей и руководств в других местах.)

public class MyNoCookiesInTheJarDynamicObject : DynamicObject
{
    Dictionary<string, object> properties = new Dictionary<string, object>();

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (properties.ContainsKey(binder.Name))
        {
            result = properties[binder.Name];
            return true;
        }
        else
        {
            result = "I'm sorry, there are no cookies in this jar!"; //<-- THIS IS OUR 
            CUSTOM "NO COOKIES IN THE JAR" RESPONSE FROM OUR DYNAMIC TYPE WHEN AN UNKNOWN FIELD IS ACCESSED
            return false;
        }
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        properties[binder.Name] = value;
        return true;
    }

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        dynamic method = properties[binder.Name];
        result = method(args[0].ToString(), args[1].ToString());
        return true;
    }
}

Теперь мы можем использовать этот воображаемый класс, который мы только что создали, как динамический тип, который имеет очень нестандартное поведение, если поле не существует.

dynamic d = new MyNoCookiesInTheJarDynamicObject();
var s = d.FieldThatDoesntExist;

//in our contrived example, the below should evaluate to true
Assert.IsTrue(s == "I'm sorry, there are no cookies in this jar!")

ExpandoObject - это ПОЛНАЯ реализация IDynamicMetaObjectProvider, где команда .NET Framework приняла все эти решения за вас. Это полезно, если вам не нужно никакого пользовательского поведения, и вы чувствуете, что ExpandoObject работает достаточно хорошо для вас (90% времени, ExpandoObject достаточно хорошо). Так, например, см. Следующее, и что для ExpandoObject разработчики решили создать исключение, если динамический элемент не существует.

dynamic d = new ExpandoObject();

/*
The ExpandoObject designers chose that this operation should result in an 
Exception. They did not have to make that choice, null could 
have been returned, for example; or the designers could've returned a "sorry no cookies in the jar" response like in our custom class. However, if you choose to use 
ExpandoObject, you have chosen to go with their particular implementation 
of DynamicObject behavior.
*/

try {
var s = d.FieldThatDoesntExist;
}
catch(RuntimeBinderException) { ... }

Итак, подведем итог: ExpandoObject - это просто один предварительно выбранный способ расширения DynamicObject с определенными динамическими режимами диспетчеризации, который, вероятно, будет работать для вас , но может не зависеть от ваших конкретных потребностей.

Принимая во внимание, что DyanmicObject - это вспомогательный базовый тип, упрощающий и упрощающий реализацию ваших собственных типов с уникальными динамическими поведениями.

Полезное руководство, на котором основана большая часть приведенного выше примера источника.

34 голосов
/ 25 августа 2010

В соответствии со спецификацией языка C # dynamic является объявлением типа. То есть dynamic x означает, что переменная x имеет тип dynamic.

DynamicObject - это тип, который упрощает реализацию IDynamicMetaObjectProvider и, таким образом, переопределяет специфическое поведение привязки для типа.

ExpandoObject - это тип, который действует как сумка свойств. То есть вы можете добавлять свойства, методы и т. д. к динамическим экземплярам этого типа во время выполнения.

0 голосов
/ 25 января 2018

Приведенный выше пример DynamicObject не дает четкого различия, поскольку он в основном реализует функциональность, которая уже предоставляется ExpandoObject.

В двух ссылках, упомянутых ниже, очень яснос помощью DynamicObject можно сохранить / изменить фактический тип (XElement в примере, используемом в ссылках ниже) и улучшить контроль над свойствами и методами.

https://blogs.msdn.microsoft.com/csharpfaq/2009/09/30/dynamic-in-c-4-0-introducing-the-expandoobject/

https://blogs.msdn.microsoft.com/csharpfaq/2009/10/19/dynamic-in-c-4-0-creating-wrappers-with-dynamicobject/

public class DynamicXMLNode : DynamicObject    
{    
    XElement node;

    public DynamicXMLNode(XElement node)    
    {    
        this.node = node;    
    }

    public DynamicXMLNode()    
    {    
    }

    public DynamicXMLNode(String name)    
    {    
        node = new XElement(name);    
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)    
    {    
        XElement setNode = node.Element(binder.Name);

        if (setNode != null)    
            setNode.SetValue(value);    
        else    
        {    
            if (value.GetType() == typeof(DynamicXMLNode))    
                node.Add(new XElement(binder.Name));    
            else    
                node.Add(new XElement(binder.Name, value));    
        }

        return true;    
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)    
    {    
        XElement getNode = node.Element(binder.Name);

        if (getNode != null)    
        {    
            result = new DynamicXMLNode(getNode);    
            return true;    
        }    
        else    
        {    
            result = null;    
            return false;    
        }    
    }    
}
...