This article is currently in the process of being translated into Chinese (~86% done).
The ExpandoObject
上一章已介绍过,可以用dynamic关键字定义的变量指向一个不用先定义类而通过直接定义属性得到的对象。动态对象不能在对象初始化后再增加属性。如果需要此功能,C#还是有方法可以实现的:使用ExpandoObject。直接来个例子,看看有多简单:
dynamic user = new System.Dynamic.ExpandoObject();
user.Name = "John Doe";
user.Age = 42;
user.HomeTown = "New York";
Console.WriteLine(user.Name + " is " + user.Age + " years old and lives in " + user.HomeTown); 注意这里定义的对象类型为dynamic,虽然实例化的是一个ExpandoObject对象。原因是当对象被声明为ExpandoObject时,编译器会进行检查,并立即因找不到后面凭空加上的属性(姓名,年龄等等)而报错。如上一章介绍的那样,把变量定义为dynamic类型就避免了编译器介入,不再检查代码所用的属性是否存在。
ExpandoObject的属性当然也可以是ExpandoObject类型,这相当厉害,可以凭空实现复杂的类型,象这个例子:
dynamic user = new System.Dynamic.ExpandoObject();
user.Name = "John Doe";
user.Age = 42;
user.HomeTown = new System.Dynamic.ExpandoObject();
user.HomeTown.Name = "New York";
user.HomeTown.ZipCode = 10001;
Console.WriteLine(user.Name + " is " + user.Age + " years old and lives in " + user.HomeTown.Name + " [" + user.HomeTown.ZipCode + "]");这里只是简单地把HomeTown属性的类型从sring改为ExpandoObject,然后再在其上增加属性,姓名和邮编。但这还远未结束 - 甚至还能在此对象上增加方法,同样是凭空直接增加,当然这需要相当复杂的技巧:
user.DescribeUser = (Func<String>)(() => {
return user.Name + " is " + user.Age + " years old and lives in " + user.HomeTown.Name + " [" + user.HomeTown.ZipCode + "]";
});
Console.WriteLine(user.DescribeUser());很强大吧!不过到底ExpandoObject的本质是什么呢?它实现了一些有趣的界面,其中就包括 IDictionary<string, object> - 这意味着藏在所有这些语法便利性外表下的ExpandoObject其实只是一个dictionary(字典),通过string类型的键来索引对象。这也表示要迭代ExpandoObject内的项目与迭代一个常规的Dictionary是一样易容的。这个功能很实用:
dynamic user = new System.Dynamic.ExpandoObject();
user.Name = "John Doe";
user.Age = 42;
foreach (KeyValuePair<string, object> kvp in user)
{
Console.WriteLine(kvp.Key + ": " + kvp.Value);
}总结
ExpandoObject允许凭空定义对象,然后随意在其上增加属性。由于它实际上是个动态类型,也就继承了上一章中讨论过的动态类型的一些好处和坏处。不过ExpandoObject实现了INotifyPropertyChanged界面,这绝对是项人见人爱的好处,在程序中使用过,如,WPF的人就知道了。