Различия между ExpandoObject, DynamicObject и динамическими

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

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

Ответ 1

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

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

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

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

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

Ответ 2

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

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

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

Ответ 3

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

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

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

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

  • DynamicObject не может быть сконструирован напрямую.
  • Вы ДОЛЖНЫ расширить DynamicObject, чтобы он мог использовать вас как разработчика.
  • При расширении DynamicObject теперь вы можете предоставлять ПОСТОЯННОЕ поведение относительно того, как вы хотите, чтобы динамическая отправка была разрешена для данных, хранящихся внутри вашего базового представления данных во время выполнения.
  • ExpandoObject хранит базовые данные в словаре и т.д. Если вы реализуете DynamicObject, вы можете хранить данные везде, где угодно и как угодно. (например, как вы получаете и устанавливаете данные при отправке, полностью зависит от вас).

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

Пример. Представьте, что вы хотите иметь динамический тип, который возвращает настраиваемый по умолчанию всякий раз, когда попытка get пытается получить элемент, который НЕ существует (т.е. не был добавлен во время выполнения). И этот дефолт скажет: "Извините, в этой банке нет куки!". Если вам нужен динамический объект, который ведет себя так, вам нужно будет контролировать, что происходит, когда поле не найдено. 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, который упрощает и упрощает реализацию ваших собственных типов с уникальным динамическим поведением.

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