ExpandoObject,DynamicObject和dynamic之间的区别


170

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

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

Answers:


154

dynamic关键字用于声明应该后期绑定变量。
如果要对任何实类型或想象中的类型使用后期绑定,请使用dynamic关键字,其余的由编译器完成。

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

IDynamicMetaObjectProvider接口允许类控制其后期行为。
当您使用dynamic关键字与IDynamicMetaObjectProvider实现交互时,DLR会调用IDynamicMetaObjectProvider方法,而对象本身将决定要执行的操作。

ExpandoObjectDynamicObject类的实现IDynamicMetaObjectProvider

ExpandoObject是一个简单的类,它允许您将成员添加到实例并使用它们dynamic
DynamicObject是一种更高级的实现,可以继承以轻松提供自定义行为。


2
在哪个地方可以了解更多有关此的好地方?不是API,而是为什么要使用API​​?例如,为什么ExpandoObject不从DynamicObject派生,DynamicObject看起来是ruby基于'method_missing'编程的事实上的基本类型。
Gishu 2012年

4
您能否在可能的地方添加一些用法示例?例如,我将如何使用DynamicObject,有什么好处?
2014年

10
没有这样的例子的好答案就像蛋糕上面没有奶油。
Teoman shipahi 2015年


68

我将尝试为该问题提供一个更清晰的答案,以清楚地解释动态ExpandoObject和之间的区别DynamicObject

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

ExpandoObject并且DynamicObject确实是类型。在表面上,它们看起来非常相似。这两个类都实现IDynamicMetaObjectProvider。但是,深入研究,您会发现它们根本不相似。

DynamicObject是部分实现,IDynamicMetaObjectProvider纯粹是为了作为开发人员实现自己的自定义类型的起点,该自定义类型支持具有自定义基础存储和检索行为的动态调度,以使动态调度工作。

  1. DynamicObject无法直接构造。
  2. 您必须扩展DynamicObject才能使其成为开发人员。
  3. 扩展DynamicObject时,您现在可以提供有关如何使动态调度在运行时解析为内部存储在基础数据表示形式中的数据的自定义行为。
  4. ExpandoObject将基础数据存储在Dictionary等中。如果实现DynamicObject,则可以在任意位置存储数据,但是可以根据需要存储数据。(例如,如何分配信息以及如何设置数据完全取决于您)。

简而言之,当您要创建可与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的一种预先选择的方法,这可能对您有用,但可能并不取决于您的特定需求。

DyanmicObjecthelper BaseType则使实现具有独特动态行为的自己的类型变得简单而容易。

上面的许多示例源都基于此有用的教程。


很好的解释。只是一项技术更正:ExpandoObject并非从DynamicObject继承。
Mike Rosoft

对示例的一个小更正DynamicObject:覆盖时TryGetMember,如果返回false,则RuntimeBinderException尝试访问不存在的属性时将抛出a 。为了使代码片段真正起作用,您应该返回true
lluchmk

36

根据C#语言规范,dynamic是一个类型声明。即dynamic x表示变量x具有类型dynamic

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

ExpandoObject是一种类似于财产袋的类型。也就是说,您可以在运行时将属性,方法等添加到此类型的动态实例中。


25
dynamic不是实际的类型...只是提示,告诉编译器对此变量使用后期绑定。dynamic变量实际上是object在MSIL中声明的
Thomas Levesque 2010年

1
@Thomas:从编译器的角度来看,它是一种类型,但是您说对了,运行时表示形式是Object的表示形式。您将在多个MS演示文稿中找到“静态类型为动态类型”语句。
布赖恩·拉斯穆森

3
@Thomas:语言规范说“ C#4.0引入了一个称为动态的新静态类型”。
布赖恩·拉斯穆森

确实...但是我认为将其视为类型会令人困惑,因为与诸如DynamicObject或ExpandoObject之类的类型没有继承关系
Thomas Levesque 2010年

3
@NathanA我和你在一起。但是,语言规范将其称为类型,这就是我要使用的类型。
Brian Rasmussen 2014年

0

上面的示例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;    
        }    
    }    
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.