Answers:
该dynamic
关键字用于声明应该后期绑定变量。
如果要对任何实类型或想象中的类型使用后期绑定,请使用dynamic
关键字,其余的由编译器完成。
当您使用dynamic
关键字与普通实例进行交互时,DLR将对实例的普通方法执行后期绑定调用。
该IDynamicMetaObjectProvider
接口允许类控制其后期行为。
当您使用dynamic
关键字与IDynamicMetaObjectProvider
实现交互时,DLR会调用IDynamicMetaObjectProvider
方法,而对象本身将决定要执行的操作。
在ExpandoObject
和DynamicObject
类的实现IDynamicMetaObjectProvider
。
ExpandoObject
是一个简单的类,它允许您将成员添加到实例并使用它们dynamic
。
DynamicObject
是一种更高级的实现,可以继承以轻松提供自定义行为。
我将尝试为该问题提供一个更清晰的答案,以清楚地解释动态ExpandoObject
和之间的区别DynamicObject
。
很快,dynamic
就是一个关键字。它本身不是类型。它是一个关键字,告诉编译器在设计时忽略静态类型检查,而在运行时使用后期绑定。因此,dynamic
在此答案的其余部分中,我们不会花很多时间。
ExpandoObject
并且DynamicObject
确实是类型。在表面上,它们看起来非常相似。这两个类都实现IDynamicMetaObjectProvider
。但是,深入研究,您会发现它们根本不相似。
DynamicObject是部分实现,IDynamicMetaObjectProvider
纯粹是为了作为开发人员实现自己的自定义类型的起点,该自定义类型支持具有自定义基础存储和检索行为的动态调度,以使动态调度工作。
简而言之,当您要创建可与DLR一起使用并使用所需的任何CUSTOM行为的OWN类型时,请使用DynamicObject。
示例:假设您希望有一个动态类型,该动态类型在尝试对不存在的成员(即,在运行时未添加)进行get时返回一个自定义默认值。该默认值将显示“对不起,此罐子中没有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
。.NETFramework团队已为您做出所有这些决定。如果您不需要任何自定义行为,并且您觉得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
helper BaseType则使实现具有独特动态行为的自己的类型变得简单而容易。
DynamicObject
:覆盖时TryGetMember
,如果返回false,则RuntimeBinderException
尝试访问不存在的属性时将抛出a 。为了使代码片段真正起作用,您应该返回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;
}
}
}