ExpandoObject的真正好处是什么?


587

添加到.NET 4 的ExpandoObject类使您可以在运行时将属性任意设置到对象上。

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

例如,为什么这样:

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而不是只使用一个任意字典类型,比不是很明显,您使用的类型是怎么回事在运行时确定的其他获得。

Answers:


689

自从我写了您所指的MSDN文章以来,我想我必须回答这一问题。

首先,我预料到了这个问题,这就是为什么我写了一篇博客文章,该文章或多或少显示了ExpandoObject的实际用例:C#4.0中的Dynamic: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可以使用而不是使用,EventHandler<dynamic>这将导致处理程序的第二个参数为dynamic


53
有趣。感谢您提供有关re:事件的信息。对我来说,那是一个新事物。
Reed Copsey

16
@AlexandraRusina,当您说时它怎么知道这是一个事件d.MyEvent = null;,否则就不知道?
Shimmy Weitzhandler

20
也许我缺少了一些东西,但这不是事件-这是委托类型的简单属性。
Sergey Berezovskiy

7
可以使用匿名类型编写第一个代码块:var expando = new { Address = new { State = "WA" } }; Console.WriteLine(expando.Address.State);我发现这更易读,但是非常实用。鉴于它是静态类型的,因此在这种情况下更有用。
nawfal

13
@nawfal多数民众赞成在不正确-匿名与Expando不同。您正在创建一个匿名类型,该匿名类型不能再向其中添加任意属性。
Blowhard博士2014年

75

一种优势是绑定方案。数据网格和属性网格将通过TypeDescriptor系统获取动态属性。此外,WPF数据绑定将了解动态属性,因此WPF控件比字典更易于绑定到ExpandoObject。

在某些情况下,可能还会考虑与动态语言的互操作性,这将期望DLR属性而不是字典条目。


6
似乎绑定到动态对象被打破。报告用户的eisenbergeffect在caliburn.micro的SO和协调器上。@AlexandraRusina您可以评论错误的状态和“无法修复”状态

2
对于那些好奇的人,我目前可以绑定List<dynamic>IEnumerable<dynamic>使用WPF4
Graham Bass

47

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

public dynamic SomeData { get; set; }

...

SomeData.WhatEver = "Yo Man!";

...

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

28

与其他基于Interop的语言互操作DLR是我能想到的#1原因。您不能通过它们,Dictionary<string, object>因为它不是IDynamicMetaObjectProvider。另一个额外的好处是它实现了INotifyPropertyChanged,这意味着在WPF的数据绑定世界中,它还提供了超出Dictionary<K,V>您所能提供的其他好处。


19

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


9
@J。亨德里克斯,别忘了他也说过“肮脏”。Intellisense有其缺点,但是,它使调试和错误捕获更加容易。我个人还是喜欢静态类型而不是动态类型,除非我要处理一个奇怪的(而且总是很少见)的情况。
菲尔(Phil)

+1是为了方便。但是,我发现匿名类型可以像一个简单的属性包一样方便,并且其静态性更好。
nawfal

1
我不想在生产代码中使用它,但是它在测试代码中非常方便,并且可以使其看起来非常漂亮。
Tobias 2014年

14

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

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


11

这是MSDN上一篇很棒的文章的示例,该文章关于使用ExpandoObject为传入的结构化数据(例如XML,Json)创建动态临时类型。

我们还可以将委托分配给ExpandoObject的dynamic属性:

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表达式,闭包,dynamic关键字和DynamicObject类一起,我们可以将函数式编程的某些元素引入C#代码,我们从诸如JavaScript或PHP的动态语言中了解到这些。


4

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

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

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

因此,直接回答您的问题:编写起来更容易阅读。但是从技术上讲,它本质上是一个Dictionary<string,object>(您甚至可以将其转换为一个以列出值)。


-1
var obj = new Dictionary<string, object>;
...
Console.WriteLine(obj["MyString"]);

我认为这仅是可行的,因为所有内容都具有ToString(),否则,您必须知道它的类型,然后将“对象”转换为该类型。


其中一些比其他一些有用,我正在尝试更详尽。

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

  2. 似乎可以将其用作一个非常好的元组。您仍然可以将您的成员称为“ Item1”,“ Item2”等...,但现在不必如此,与Tuple不同,它也是可变的。这确实具有缺乏智能支持的巨大缺点。

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

  4. 您可以为ExpandoObject本身或其成员分配值吗?与dynamic / dynamic []进行比较和对比,使用最适合您的需求。

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

  6. 您不能将dynamic用作类中的数据成员,也许是因为它至少有点像关键字,希望您可以使用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...

-3

在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));

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


1
从值元组不能写Tc = 5的角度来看,您的示例并不相同。完成定义T之后。由于ExpandoObject是动态的,因此可以使用它。具有值元组的示例与声明匿名类型非常相似。例如:var T2 = new {x = 1,y = 2,z = new {a = 3,b = 4}};
LxL

为什么我需要写Tc = 5而不定义它?ExpandoObject仅在处理.net中未定义的COM对象时才有用。否则,我永远不要使用此ExpandoObject,因为它在设计时和运行时都很脏而且有错误。
工程。M.Hamdy

1
首先将z分配给(a:3,b:4),然后再希望z具有其他c属性怎么样?您可以使用值元组吗?
LxL

因此,我的观点是,您无法将ExpandoObject与值元组进行比较,因为它们是为不同目的而设计的。通过比较您的方式,您忽略了ExpandoObject设计用于动态结构的功能。
LxL
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.