对象、动态对象和动态对象的区别

System.Dynamic.ExpandoObjectSystem.Dynamic.DynamicObjectdynamic之间有什么区别?

您在哪些情况下使用这些类型?

57812 次浏览

根据 C # 语言规范,dynamic是一个类型声明。也就是说,dynamic x表示变量 x的类型为 dynamic

DynamicObject是一种易于实现 IDynamicMetaObjectProvider的类型,因此可以覆盖该类型的特定绑定行为。

ExpandoObject是一种类似于属性袋的类型。也就是说,您可以在运行时向这种类型的动态实例添加属性、方法等等。

dynamic关键字用于声明应该进行后期绑定的变量。
如果您想使用后期绑定,对于任何实际或想象的类型,您都可以使用 dynamic关键字,编译器会完成其余的工作。

当您使用 dynamic关键字与普通实例进行交互时,DLR将对实例的普通方法执行后期绑定调用。

IDynamicMetaObjectProvider接口允许类控制其后期绑定行为。
当您使用 dynamic关键字与 IDynamicMetaObjectProvider实现交互时,DLR 调用 IDynamicMetaObjectProvider方法,对象自己决定做什么。

ExpandoObjectDynamicObject类是 IDynamicMetaObjectProvider的实现。

ExpandoObject是一个简单的类,它允许您将成员添加到实例并在 dynamically 中使用它们。
DynamicObject是一个更高级的实现,它可以被继承以便于提供定制的行为。

我将尝试提供一个更清晰的答案这个问题,以清楚地解释什么是动态之间的差异,ExpandoObjectDynamicObject

很快,dynamic是一个关键词。它本身不是一种类型。它是一个关键字,告诉编译器在设计时忽略静态类型检查,而在运行时使用后期绑定。所以在这个答案的其余部分,我们不会花太多时间在 dynamic上。

ExpandoObjectDynamicObject确实是类型。从表面上看,它们彼此非常相似。这两个类都实现了 IDynamicMetaObjectProvider。然而,深入挖掘,你会发现他们根本不相似。

DynamicObject 是 IDynamicMetaObjectProvider的部分实现,纯粹是为了让开发人员能够实现自己的定制类型,支持带有定制底层存储和检索行为的克劳斯·福尔曼克劳斯·福尔曼。

  1. DynamicObject 不能直接构造。
  2. 您必须扩展 DynamicObject,使其对您作为开发人员有任何用处。
  3. 在扩展 DynamicObject 时,您现在可以提供关于在运行时如何将克劳斯·福尔曼解析为内部存储在基础数据表示中的数据的定制行为。
  4. ExpandoObject 将底层数据存储在 Dictionary 中,等等。如果实现 DynamicObject,则可以随时随地存储数据。(例如,如何获取和设置调度数据完全取决于您自己)。

简而言之,当您想要创建可以与 DLR 一起使用的 OWN 类型并且可以使用您喜欢的任何定制行为时,请使用 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!")

ExpandoObjectIDynamicMetaObjectProvider的完整实现,其中。NETFramework 团队已经为您做出了所有这些决定。如果您不需要任何自定义行为,并且您觉得 ExpandObject 对您来说已经足够好了(在90% 的情况下,ExpandoObject已经足够好了) ,那么这是非常有用的。例如,请参阅以下内容,以及对于 ExpandObject,设计人员选择在动态成员不存在时抛出异常。

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示例没有清楚地说明其中的区别,因为它基本上实现了 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;
}
}
}