Відповіді:
dynamic
Ключове слово використовується для оголошення змінних , які повинні бути пізнім зв'язуванням.
Якщо ви хочете використовувати пізнє прив'язування, для будь-якого реального або уявного типу, ви використовуєте dynamic
ключове слово, а компілятор робить все інше.
Коли ви використовуєте dynamic
ключове слово для взаємодії зі звичайним екземпляром, DLR виконує пізні обмежені виклики до звичайних методів цього примірника.
IDynamicMetaObjectProvider
Інтерфейс дозволяє класу взяти під контроль його пізній пов'язане поведінки.
Коли ви використовуєте dynamic
ключове слово для взаємодії з IDynamicMetaObjectProvider
реалізацією, DLR викликає IDynamicMetaObjectProvider
методи, і сам об'єкт вирішує, що робити.
ExpandoObject
І DynamicObject
класи реалізації IDynamicMetaObjectProvider
.
ExpandoObject
це простий клас, який дозволяє додавати членів до екземпляра та використовувати їх dynamic
союзниками.
DynamicObject
це більш просунута реалізація, яка може бути успадкована, щоб легко забезпечити індивідуальну поведінку.
Я спробую дати більш чітку відповідь на це запитання, щоб чітко пояснити, які відмінності між динамічним ExpandoObject
та DynamicObject
.
Дуже швидко, dynamic
це ключове слово. Це не тип per se. Це ключове слово, яке вказує компілятору ігнорувати перевірку статичних типів під час проектування та замість цього використовувати пізнє прив'язування під час виконання. Тому ми не будемо витрачати багато часу на dynamic
решту цієї відповіді.
ExpandoObject
і DynamicObject
справді є типами. На ПОВЕРХНІ вони дуже схожі один на одного. Обидва класи реалізують IDynamicMetaObjectProvider
. Однак копайся глибше, і ти виявиш, що вони зовсім НЕ схожі.
DynamicObject - це часткова реалізація, IDynamicMetaObjectProvider
суто призначена для відпрацьованого пункту для розробників для впровадження власних користувацьких типів, що підтримують динамічну диспетчеризацію з використанням базових даних щодо зберігання та пошуку, щоб зробити динамічну роботу диспетчеризації.
Коротше кажучи, використовуйте 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, який робить реалізацію власних типів з унікальною динамічною поведінкою простою та легкою.
Корисний підручник, на якому базується значна частина наведеного вище джерела.
DynamicObject
: при переосмисленні TryGetMember
, якщо ви повернете помилкове RuntimeBinderException
заповіт, буде викинуто при спробі доступу до неіснуючого ресурсу Щоб фрагмент фактично працював, ви повинні повернутися true
.
Відповідно до специфікації мови C # dynamic
- це декларація про тип. Тобто dynamic x
змінна x
має тип dynamic
.
DynamicObject
це тип, який спрощує реалізацію IDynamicMetaObjectProvider
і, таким чином, переорієнтовує специфічну поведінку прив'язки для типу.
ExpandoObject
це тип, який діє як мішок власності. Тобто ви можете додавати властивості, методи та інше до динамічних примірників цього типу під час виконання.
dynamic
не є фактичним типом ... це лише підказка сказати компілятору використовувати пізнє прив'язування для цієї змінної. dynamic
змінні фактично оголошені як object
у MSIL
Наведений вище приклад DynamicObject
чітко не визначає різницю, оскільки це в основному реалізація функціоналу, який вже надається ExpandoObject
.
У двох згаданих нижче посиланнях дуже зрозуміло, що за допомогою DynamicObject
можна зберегти / змінити фактичний тип ( XElement
у прикладі, що використовується у наведених нижче посиланнях) та покращити контроль над властивостями та методами.
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;
}
}
}