Відмінності між ExpandoObject, DynamicObject і динамічним


170

Чим відрізняються System.Dynamic.ExpandoObject, System.Dynamic.DynamicObjectі dynamic?

У яких ситуаціях ви використовуєте ці типи?

Відповіді:


154

dynamicКлючове слово використовується для оголошення змінних , які повинні бути пізнім зв'язуванням.
Якщо ви хочете використовувати пізнє прив'язування, для будь-якого реального або уявного типу, ви використовуєте dynamicключове слово, а компілятор робить все інше.

Коли ви використовуєте dynamicключове слово для взаємодії зі звичайним екземпляром, DLR виконує пізні обмежені виклики до звичайних методів цього примірника.

IDynamicMetaObjectProviderІнтерфейс дозволяє класу взяти під контроль його пізній пов'язане поведінки.
Коли ви використовуєте dynamicключове слово для взаємодії з IDynamicMetaObjectProviderреалізацією, DLR викликає IDynamicMetaObjectProviderметоди, і сам об'єкт вирішує, що робити.

ExpandoObjectІ DynamicObjectкласи реалізації IDynamicMetaObjectProvider.

ExpandoObjectце простий клас, який дозволяє додавати членів до екземпляра та використовувати їх dynamicсоюзниками.
DynamicObjectце більш просунута реалізація, яка може бути успадкована, щоб легко забезпечити індивідуальну поведінку.


2
Що було б хорошим місцем, щоб дізнатися більше про це? Не API, а чому за API? наприклад, чому ExpandoObject не походить від DynamicObject, який виглядає базовим типом defacto для програмування на основі методу "russ" на базі методу ".
Гішу

4
Чи можете ви додати кілька прикладів використання, де це можливо? Наприклад, як я можу використовувати DynamicObject і які переваги?
квітня

10
Чудові відповіді без подібних прикладів - це торт без вершків.
Теоман шипахі


68

Я спробую дати більш чітку відповідь на це запитання, щоб чітко пояснити, які відмінності між динамічним ExpandoObjectта DynamicObject.

Дуже швидко, dynamicце ключове слово. Це не тип per se. Це ключове слово, яке вказує компілятору ігнорувати перевірку статичних типів під час проектування та замість цього використовувати пізнє прив'язування під час виконання. Тому ми не будемо витрачати багато часу на dynamicрешту цієї відповіді.

ExpandoObjectі DynamicObjectсправді є типами. На ПОВЕРХНІ вони дуже схожі один на одного. Обидва класи реалізують IDynamicMetaObjectProvider. Однак копайся глибше, і ти виявиш, що вони зовсім НЕ схожі.

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

  1. DynamicObject неможливо побудувати безпосередньо.
  2. Ви ОБОВ'ЯЗКОВО розширити DynamicObject, щоб він мав користь для Вас як розробника.
  3. Коли ви розширюєте DynamicObject, тепер ви можете надати CUSTOM поведінку щодо того, як ви хочете, щоб динамічна відправка вирішувала дані, що зберігаються всередині вашого базового представлення даних під час виконання.
  4. ExpandoObject зберігає основні дані у словнику тощо. Якщо ви реалізуєте DynamicObject, ви можете зберігати дані куди завгодно і як завгодно. (наприклад, як ви отримуєте та встановлюєте дані при відправці, повністю залежить від вас).

Коротше кажучи, використовуйте DynamicObject, коли ви хочете створити свої власні типи, які можна використовувати з DLR та працювати з будь-якою CUSTOM поведінкою, яку б ви хотіли.

Приклад. Уявіть, що ви хочете мати динамічний тип, який повертає спеціальний за замовчуванням кожен раз, коли буде здійснено спробу отримання члена, який НЕ існує (тобто не додано під час виконання). І за замовчуванням буде сказано: "Вибачте, у цій банці немає печива!". Якщо ви хочете динамічний об'єкт, який веде себе таким чином, вам потрібно буде контролювати, що відбувається, коли поле не знайдено. 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це помічник BaseType, який робить реалізацію власних типів з унікальною динамічною поведінкою простою та легкою.

Корисний підручник, на якому базується значна частина наведеного вище джерела.


Дуже хороше пояснення. Тільки одна технічна корекція: ExpandoObject не успадковує від DynamicObject.
Майк Рософт

Невелика поправка на прикладі для DynamicObject: при переосмисленні TryGetMember, якщо ви повернете помилкове RuntimeBinderExceptionзаповіт, буде викинуто при спробі доступу до неіснуючого ресурсу Щоб фрагмент фактично працював, ви повинні повернутися true.
lluchmk

36

Відповідно до специфікації мови C # dynamic- це декларація про тип. Тобто dynamic xзмінна xмає тип dynamic.

DynamicObjectце тип, який спрощує реалізацію IDynamicMetaObjectProviderі, таким чином, переорієнтовує специфічну поведінку прив'язки для типу.

ExpandoObjectце тип, який діє як мішок власності. Тобто ви можете додавати властивості, методи та інше до динамічних примірників цього типу під час виконання.


25
dynamicне є фактичним типом ... це лише підказка сказати компілятору використовувати пізнє прив'язування для цієї змінної. dynamicзмінні фактично оголошені як objectу MSIL
Thomas Levesque

1
@Thomas: з точки зору компілятора це тип, але ви маєте рацію, що представлення часу виконання - це Object. Вислів "статично набраний для динамічного" ви знайдете в кількох презентаціях MS.
Брайан Расмуссен

3
@Thomas: і мовна специфікація говорить "C # 4.0 вводить новий статичний тип, який називається динамічним".
Брайан Расмуссен

дійсно ... Але я думаю, що це заплутано розглядати це як тип, оскільки немає спадкового відношення до таких типів, як DynamicObject або ExpandoObject
Thomas Levesque

3
@NathanA Я тут з тобою. Однак специфікація мови називає її типом, тож саме з цим я йду.
Брайан Расмуссен

0

Наведений вище приклад 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;    
        }    
    }    
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.