System.Dynamic.ExpandoObject
,System.Dynamic.DynamicObject
和dynamic
之间有什么区别?
您在哪些情况下使用这些类型?
答案 0 :(得分:136)
dynamic
关键字用于声明应该是后期绑定的变量
如果你想使用后期绑定,对于任何真实或想象的类型,你使用dynamic
关键字,编译器完成其余的工作。
当您使用dynamic
关键字与普通实例进行交互时,DLR会对实例的常规方法执行后期绑定调用。
IDynamicMetaObjectProvider
interface允许班级控制其后期行为
当您使用dynamic
关键字与IDynamicMetaObjectProvider
实现进行交互时,DLR会调用IDynamicMetaObjectProvider
方法,对象本身会决定要执行的操作。
ExpandoObject
和DynamicObject
类是IDynamicMetaObjectProvider
的实现。
ExpandoObject
是一个简单的类,它允许您将成员添加到实例并使用它们dynamic
盟友。
DynamicObject
是一种更高级的实现,可以继承以轻松提供自定义行为。
答案 1 :(得分:47)
我将尝试为此问题提供更清晰的答案,以清楚地解释动态ExpandoObject
和DynamicObject
之间的差异。
很快,dynamic
是一个关键字。它本身不是一种类型。它是一个关键字,告诉编译器在设计时忽略静态类型检查,而不是在运行时使用后期绑定。因此,在本回答的其余部分中,我们不会在dynamic
上花费太多时间。
ExpandoObject
和DynamicObject
确实是类型。在SURFACE上,它们看起来非常相似。这两个类都实现了IDynamicMetaObjectProvider
。但是,深入挖掘,你会发现它们根本不相似。
DynamicObject是IDynamicMetaObjectProvider
的部分实现,纯粹是开发人员实现自己的自定义类型的起点,支持使用自定义底层存储和检索行为进行动态调度,以使动态调度工作。强>
简而言之,当您想要创建可与DLR一起使用的OWN类型并使用您想要的任何CUSTOM行为时,请使用DynamicObject。
示例:假设您想要一个动态类型,只要在不存在的成员上尝试获取(即在运行时尚未添加),就会返回自定义默认值。那个默认会说,“对不起,这个罐子里没有饼干!”。如果您想要一个行为类似的动态对象,则需要控制未找到字段时发生的情况。 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,它可以简单易用地实现您自己的类型,具有独特的动态行为。
A useful tutorial on which much of the example source above is based.
答案 2 :(得分:34)
根据C#语言规范dynamic
是一个类型声明。即dynamic x
表示变量x
的类型为dynamic
。
DynamicObject
是一种类型,可以轻松实现IDynamicMetaObjectProvider
,从而覆盖该类型的特定绑定行为。
ExpandoObject
是一种类似于属性包的类型。即您可以在运行时向此类型的动态实例添加属性,方法等。
答案 3 :(得分:0)
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;
}
}
}