添加到.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而不仅仅使用任意字典类型可以获得什么真正的优势,除了不明显你正在使用将在运行时确定的类型。
自从我写了你所指的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;
if (e != null)
{
e(d, new EventArgs());
}
// We could also fire it with...
// d.MyEvent(d, new EventArgs());
// ...if we knew for sure that the event is non-null.
}
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);
}
}
在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));
除了使用元组语法,你还有强大的类型推断和intlisense支持
一个优点是绑定方案。数据网格和属性网格将通过TypeDescriptor系统获取动态属性。此外,WPF数据绑定将理解动态属性,因此WPF控件可以比字典更容易绑定到ExpandoObject。
在某些情况下,可能还需要考虑与动态语言的互操作性,这些语言将期望DLR属性而不是字典条目。
对我来说真正的好处是来自XAML的完全轻松的数据绑定:
public dynamic SomeData { get; set; }
...
SomeData.WhatEver = "Yo Man!";
...
<TextBlock Text="{Binding SomeData.WhatEver}" />
与DLR
建立的其他语言互操作是我能想到的第一个原因。你不能通过他们Dictionary<string, object>
因为它不是IDynamicMetaObjectProvider
。另一个额外的好处是它实现了INotifyPropertyChanged
,这意味着在WPF的数据绑定世界中,它还具有超出Dictionary<K,V>
可以为您提供的额外好处。
这一切都与程序员的便利性有关。我可以想象用这个对象编写快速和脏的程序。
我认为它会有一个语法上的好处,因为你不再通过使用字典来“伪装”动态添加的属性。
那,并且我会想到与动态语言互操作。
这是伟大的MSDN article关于使用ExpandoObject为传入的结构化数据(即XML,Json)创建动态ad-hoc类型的例子。
我们还可以将委托分配给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。
在某些情况下这很方便。例如,我将它用于Modularized shell。每个模块都定义了自己的配置对话框,其数据包含在其设置中。我为它提供了一个ExpandoObject作为Datacontext,并将值保存在我的配置存储中。这样,配置对话框编写器只需绑定到一个值,它就会自动创建并保存。 (并提供给模块使用这些设置当然)
它比字典更容易使用。但是每个人都应该意识到内部它只是一个字典。
这就像LINQ只是语法糖,但它有时会让事情变得容易。
所以直接回答你的问题:它更容易编写,更容易阅读。但从技术上讲,它本质上是一个Dictionary<string,object>
(你甚至可以把它拼成一个列出值)。
var obj = new Dictionary<string, object>;
...
Console.WriteLine(obj["MyString"]);
我认为只有一切都有效,因为所有东西都有一个ToString(),否则你必须知道它的类型并将'对象'转换为该类型。
其中一些比其他更有用,我试图彻底。
如果您可以一次向下钻取多个级别,那就太好了。
var e = new ExpandoObject();
e.position.x = 5;
etc...
这不是最好的例子,想象在你自己的项目中适当的优雅用途。
很遗憾你不能让代码构建其中一些并将结果推送到intellisense。我不确定这会怎么样。
如果他们可以拥有价值和成员,那就太好了。
var fifteen = new ExpandoObject();
fifteen = 15;
fifteen.tens = 1;
fifteen.units = 5;
fifteen.ToString() = "fifteen";
etc...