【问题标题】:What are the true benefits of ExpandoObject?ExpandoObject 的真正好处是什么?
【发布时间】:2010-12-11 19:15:42
【问题描述】:

添加到 .NET 4 的 ExpandoObject 类允许您在运行时任意设置对象的属性。

与使用Dictionary<string, object> 甚至Hashtable 相比,这样做有什么优势吗?据我所知,这不过是一个哈希表,您可以使用更简洁的语法访问它。

例如,为什么会这样:

dynamic obj = new ExpandoObject();
obj.MyInt = 3;
obj.MyString = "Foo";
Console.WriteLine(obj.MyString);

确实比以下更好,或大不相同:

var obj = new Dictionary<string, object>();
obj["MyInt"] = 3;
obj["MyString"] = "Foo";

Console.WriteLine(obj["MyString"]);

使用 ExpandoObject 而不是仅仅使用任意字典类型可以获得什么真正的优势,而不是不明显地表明您正在使用将在运行时确定的类型。

【问题讨论】:

    标签: c# .net .net-4.0 c#-4.0


    【解决方案1】:

    既然我写了你指的那篇MSDN文章,我想我必须要回答这个了。

    首先,我预料到了这个问题,这就是为什么我写了一篇博文,展示了 ExpandoObject 的一个或多或少真实的用例:Dynamic in C# 4.0: Introducing the ExpandoObject

    简而言之,ExpandoObject 可以帮助您创建复杂的分层对象。例如,假设您在字典中有一个字典:

    Dictionary<String, object> dict = new Dictionary<string, object>();
    Dictionary<String, object> address = new Dictionary<string,object>();
    dict["Address"] = address;
    address["State"] = "WA";
    Console.WriteLine(((Dictionary<string,object>)dict["Address"])["State"]);
    

    层次越深,代码越丑。使用 ExpandoObject,它可以保持优雅和可读性。

    dynamic expando = new ExpandoObject();
    expando.Address = new ExpandoObject();
    expando.Address.State = "WA";
    Console.WriteLine(expando.Address.State);
    

    其次,正如已经指出的那样,ExpandoObject 实现了 INotifyPropertyChanged 接口,它让您可以比字典更多地控制属性。

    最后,您可以像这里一样向 ExpandoObject 添加事件:

    class Program
    {
       static void Main(string[] args)
       {
           dynamic d = new ExpandoObject();
    
           // Initialize the event to null (meaning no handlers)
           d.MyEvent = null;
    
           // Add some handlers
           d.MyEvent += new EventHandler(OnMyEvent);
           d.MyEvent += new EventHandler(OnMyEvent2);
    
           // Fire the event
           EventHandler e = d.MyEvent;
    
           e?.Invoke(d, new EventArgs());
       }
    
       static void OnMyEvent(object sender, EventArgs e)
       {
           Console.WriteLine("OnMyEvent fired by: {0}", sender);
       }
    
       static void OnMyEvent2(object sender, EventArgs e)
       {
           Console.WriteLine("OnMyEvent2 fired by: {0}", sender);
       }
    }
    

    另外,请记住,没有什么能阻止您以动态方式接受事件参数。换句话说,您可以使用EventHandler&lt;dynamic&gt; 而不是使用EventHandler,这将导致处理程序的第二个参数为dynamic

    【讨论】:

    • 有趣。感谢您提供信息:事件。那对我来说是一个新的。
    • @AlexandraRusina,当你说d.MyEvent = null; 时它怎么知道这是一个事件,或者它不知道?
    • 也许我遗漏了什么,但这不是事件 - 这是委托类型的简单属性。
    • 第一个块可以使用匿名类型编写:var expando = new { Address = new { State = "WA" } }; Console.WriteLine(expando.Address.State); 我觉得这更易读但 ymmv。鉴于它是静态类型的,它在这种情况下更有用。
    • @nawfal 不对——匿名与 Expando 不同。您正在创建一个匿名类型,然后它不能添加任意属性。
    【解决方案2】:

    valueTuples 之后,ExpandoObject 类有什么用? 这 6 行代码与 ExpandoObject:

    dynamic T = new ExpandoObject();
    T.x = 1;
    T.y = 2;
    T.z = new ExpandoObject();
    T.z.a = 3;
    T.b= 4;
    

    可以用元组写成一行:

    var T = (x: 1, y: 2, z: (a: 3, b: 4));
    

    除了元组语法之外,您还拥有强大的类型推断和智能支持

    【讨论】:

    • 您的示例在使用值元组的意义上并不相同,您不能编写 T.c= 5;完成定义 T 后。使用 ExpandoObject 你可以做到,因为它是动态的。您的带有值元组的示例与声明匿名类型非常相同。例如:var T2 = new { x = 1, y = 2, z = new { a = 3, b = 4 } };
    • 为什么我需要写 T.c= 5 而不定义它? ExpandoObject 仅在处理 .net 中不受限制的 COM 对象时才有用。否则,我永远不会使用此 ExpandoObject,因为它在设计时和运行时都很脏且有问题。
    • 您先将 z 分配给 (a:3,b:4) ,然后您希望 z 拥有额外的 c 属性怎么样?你可以用值元组来做吗?
    • 所以我的观点是您不能将 ExpandoObject 与值元组进行比较,因为它们是为不同目的而设计的。通过比较您的方式,您忽略了 ExpandoObject 设计的功能,即动态结构。
    【解决方案3】:

    这是来自伟大的 MSDN article 的示例,关于使用 ExpandoObject 为传入的结构化数据(即 XML、Json)创建动态临时类型。

    我们也可以将委托分配给ExpandoObject的动态属性:

    dynamic person = new ExpandoObject();
    person.FirstName = "Dino";
    person.LastName = "Esposito";
    
    person.GetFullName = (Func<String>)(() => { 
      return String.Format("{0}, {1}", 
        person.LastName, person.FirstName); 
    });
    
    var name = person.GetFullName();
    Console.WriteLine(name);
    

    因此,它允许我们在运行时将一些逻辑注入动态对象。 因此,与 lambda 表达式、闭包、动态关键字和 DynamicObject class 一起,我们可以将函数式编程的一些元素引入到 C# 代码中,我们从 JavaScript 或 PHP 等动态语言中知道这些元素。

    【讨论】:

      【解决方案4】:
      var obj = new Dictionary<string, object>;
      ...
      Console.WriteLine(obj["MyString"]);
      

      我认为这只是因为所有东西都有一个 ToString(),否则你必须知道它的类型并将“对象”转换为该类型。


      其中一些比其他的更有用,我正在努力做到彻底。

      1. 使用更直接的点表示法访问集合(在这种情况下实际上是“字典”)可能更自然。

      2. 似乎这可以用作一个非常好的元组。你仍然可以称你的成员为“Item1”、“Item2”等......但现在你不必这样做,它也是可变的,不像元组。这确实存在缺乏智能感知支持的巨大缺点。

      3. 您可能对“成员名称作为字符串”感到不舒服,就像字典的感觉一样,您可能会觉得它太像“执行字符串”,并且可能导致命名约定被编码,并且在代码尝试了解如何使用成员时处理语素和音节:-P

      4. 您可以为 ExpandoObject 本身赋值还是只为它的成员赋值?与 dynamic/dynamic[] 进行比较和对比,使用最适合您需要的。

      5. 我认为 dynamic/dynamic[] 在 foreach 循环中不起作用,您必须使用 var,但您可能可以使用 ExpandoObject。

      6. 您不能将动态用作类中的数据成员,可能是因为它至少有点像关键字,希望您可以使用 ExpandoObject。

      7. 我希望它“是”一个 ExpandoObject,可能有助于区分非常通用的事物,代码根据使用大量动态事物的类型进行区分。


      如果您可以一次钻取多个级别,那就太好了。

      var e = new ExpandoObject();
      e.position.x = 5;
      etc...
      

      这不是最好的例子,想象一下在你自己的项目中适当的优雅使用。

      很遗憾,您不能让代码构建其中的一些并将结果推送到智能感知。不过我不确定这会如何工作。

      如果他们能像成员一样有价值,那就太好了。

      var fifteen = new ExpandoObject();
      fifteen = 15;
      fifteen.tens = 1;
      fifteen.units = 5;
      fifteen.ToString() = "fifteen";
      etc...
      

      【讨论】:

        【解决方案5】:

        在某些情况下这很方便。例如,我会将它用于模块化外壳。每个模块都定义了它自己的配置对话框数据绑定到它的设置。我为它提供了一个 ExpandoObject,因为它是 Datacontext,并将值保存在我的配置存储中。这样,配置对话框编写器只需绑定到一个值,它就会自动创建和保存。 (当然也提供给模块使用这些设置)

        它比字典更容易使用。但是每个人都应该知道,在内部它只是一个字典。

        它就像 LINQ 只是语法糖,但它有时让事情变得更容易。

        所以直接回答你的问题:它更容易写,更容易阅读。但从技术上讲,它本质上是Dictionary&lt;string,object&gt;(您甚至可以将其转换为一个以列出值)。

        【讨论】:

          【解决方案6】:

          与基于DLR 的其他语言互操作是我能想到的#1 原因。您不能将Dictionary&lt;string, object&gt; 传递给他们,因为它不是IDynamicMetaObjectProvider。另一个额外的好处是它实现了INotifyPropertyChanged,这意味着在 WPF 的数据绑定世界中,它还具有超出Dictionary&lt;K,V&gt; 可以为您提供的额外好处。

          【讨论】:

            【解决方案7】:

            对我来说真正的好处是来自 XAML 的完全轻松的数据绑定:

            public dynamic SomeData { get; set; }
            

            ...

            SomeData.WhatEver = "Yo Man!";
            

            ...

             <TextBlock Text="{Binding SomeData.WhatEver}" />
            

            【讨论】:

              【解决方案8】:

              我认为它会带来语法上的好处,因为您将不再使用字典“伪造”动态添加的属性。

              那个,我想和动态语言互操作。

              【讨论】:

                【解决方案9】:

                一个优点是绑定场景。数据网格和属性网格将通过 TypeDescriptor 系统获取动态属性。此外,WPF 数据绑定将理解动态属性,因此 WPF 控件可以比字典更容易地绑定到 ExpandoObject。

                与需要 DLR 属性而不是字典条目的动态语言的互操作性在某些情况下也可能是一个考虑因素。

                【讨论】:

                • seems that databinding to dynamic objects is broken。报告用户 eisenbergeffect 在 SO 上,也是 caliburn.micro 的协调员。 @AlexandraRusina 你能评论错误的状态和状态“不会修复”
                • 对于那些好奇的人,我目前可以使用 WPF4 绑定到 List&lt;dynamic&gt;IEnumerable&lt;dynamic&gt;
                【解决方案10】:

                一切都是为了方便程序员。我可以想象用这个对象编写快速而肮脏的程序。

                【讨论】:

                • @J.亨德里克斯,别忘了他也说过“脏”。 Intellisense 有其缺点,但是,它使调试和错误捕获更容易。我个人仍然更喜欢静态类型而不是动态类型,除非我要处理一个奇怪(而且总是很少见)的情况。
                • 为方便起见+1。但是我发现匿名类型可以作为一个简单的属性包同样方便,而且它的静态性更好。
                • 我不想在生产代码中使用它,但是在测试代码中非常方便,可以让它看起来很漂亮。
                猜你喜欢
                • 2012-04-01
                • 2010-12-15
                • 2015-05-12
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2021-07-12
                • 1970-01-01
                相关资源
                最近更新 更多