C#4.0中的动态:引入ExpandoObject

Visual Studio博客

您可能已经听说过C#4.0中的新动态特性以及如何使用它来支持COM互操作。如果您没有,我强烈建议您阅读以下MSDN文章:使用动态类型如何:使用Visual C#2010功能访问Office互操作对象.

那么,你还能在哪里使用这个新功能?用例是什么?动态分派在哪里比静态类型更好?

快速回答是,每当您看到以下语法时myobject。GetProperty(“地址”),您有一个动态对象的用例。首先,上面的语法很难理解。其次,您对属性名称没有任何IntelliSense支持,如果“Address”属性不存在,则会出现运行时异常。那么为什么不创建一个动态对象,将属性调用为myobject。地址? 您仍然会得到运行时异常,并且仍然无法获得IntelliSense,但至少语法要好得多。

事实上,它不仅仅是更好的语法。您还可以获得灵活性。为了演示这种灵活性,让我们转到展开对象,这是新的动态语言运行库.展开对象实例可以在运行时添加和删除成员。你在哪里可以使用这样的物体?XML在这里是一个很好的候选者。

下面是一个代码示例MSDN(MSDN)(是的,我自己也是MSDN作家,所以我经常使用MSDN。)

X元素联系人XML=新的X元素(“联系人”,新的X元素(“姓名”,“帕特里克·海因斯”),新的X元素(“电话”,"206-555-0144"),新的X元素(“地址”,新的X元素(“街道1”,“主大街123号”),新的X元素(“城市”,“默瑟岛”),新的X元素(“状态”,“WA”(西澳大利亚州)),新的X元素(“邮政”,"68042")));

尽管LINQ to XML是一项很好的技术,我真的很喜欢它新的X元素部件看起来有点烦人。这就是我如何使用展开对象.

动态触点=新的展开对象();联系人。姓名=“帕特里克·海因斯”;联系人。电话="206-555-0144";联系人。地址=新的展开对象();联系人。地址。街道=“主大街123号”;联系人。地址。城市=“默瑟岛”;联系人。地址。州=“WA”(西澳大利亚州);联系人。地址。邮政="68402";

只需注意几件事。首先,看一下接触.

动态触点=新的展开对象();

我没有写信展开对象触点=新的展开对象(),因为如果我这样做了接触将是的静态类型对象展开对象类型。当然,静态类型的变量不能在运行时添加成员。所以我用了新的动态关键字而不是类型声明,并且自展开对象支持动态操作,代码有效。

其次,请注意,每次我需要一个节点具有子节点时,我只需创建一个新的展开对象作为接触对象。

它看起来像展开对象示例包含更多代码,但实际上更容易阅读。您可以清楚地看到每个节点包含哪些子节点,并且不需要处理括号和缩进。但最好的部分是现在如何访问元素。

这是在LINQ to XML中打印状态字段所需的代码。

慰问.WriteLine(写入行)((一串)contactXML。元素(“地址”).元素(“状态”));

这就是它的样子展开对象.

慰问.WriteLine(contact.Address.State);

但是,如果您想拥有多个Contact节点,该怎么办?如下面的LINQ to XML示例中所示。

X元素联系人XML=新的X元素(“联系人”,新的X元素(“联系人”,新的X元素(“姓名”,“帕特里克·海因斯”),新的X元素(“电话”,"206-555-0144")),新的X元素(“联系人”,新的X元素(“姓名”,“艾伦·亚当斯”),新的X元素(“电话”,“206-555-0155”)));

只需使用动态对象的集合。

动态联系人=新的列表<动态>();联络。添加(新的展开对象());联系人[0]。姓名=“帕特里克·海因斯”;联系人[0]。电话="206-555-0144";联络。添加(新的展开对象());联系人[1]。姓名=“艾伦·亚当斯”;联系人[1]。电话="206-555-0155";

从技术上讲,我会写作动态联系人=新的列表<展开对象>()和示例都可以。但是,在某些情况下,这可能会导致问题,因为列表元素的实际类型应该是动态而不是展开对象,这是两种不同的类型。(再次提及展开对象对象是静态类型的,不支持动态操作。)

现在,如果您想查找联系人列表中的所有姓名,只需迭代集合即可。

foreach公司(无功功率,无功功率c(c)在里面触点)慰问.WriteLine(c.Name);

同样,这种语法比LINQ to XML版本好。

foreach公司(无功功率,无功功率c(c)在里面联系人XML。后代(“姓名”))慰问.WriteLine(写入行)((一串)c) ;

到目前为止,一切都很好。但LINQ to XML的主要优点之一是LINQ。如何查询动态对象?尽管在这个特定领域还有很多工作要做,但您可以查询动态对象。例如,让我们查找指定名称的所有电话号码。

无功功率,无功功率电话=c(c)在里面(联系人作为列表<动态>)哪里c.名称==“帕特里克·海因斯”选择c.电话;

的确,这里的演员阵容看起来并不是绝对必要的。当然,编译器可以在运行时确定联络列表<动态>. 但正如我所说,在这方面仍有一些工作要做。

另一件需要注意的事是,这个技巧只适用于链接到对象供应商。要在LINQ To SQL或其他LINQ提供程序中使用动态对象,您需要修改提供程序本身,这是完全不同的情况。

然而,即使使用强制转换,语法仍然比LINQ to XML查询中的语法好。

无功功率,无功功率电话XML=c(c)在里面联系人XML。元素(“联系人”)哪里c.元件(“姓名”).价值==“帕特里克·海因斯”选择c.元件(“电话”).价值;

当然,LINQ to XML中有些东西看起来更好。例如,如果您想删除所有联系人的电话号码,可以在LINQ to XML中只编写一行代码。

联系人XML。元素(“联系人”).元素(“电话”).删除();

因为C#没有删除对象成员的语法,所以这里没有一个优雅的解决方案。但是展开对象实施IDictionary(图片)<字符串,对象>来维护其成员列表,您可以通过删除键-值对来删除成员。

foreach公司(无功功率,无功功率在里面触点)((IDictionary(图片)<字符串,对象>)人)。删除(“电话”);

LINQ to XML中还有其他有用的方法,比如保存()加载()。对于展开对象您需要自己编写这样的方法,但可能只需要编写一次。在这里,向IDictionary(图片)界面也有帮助。

尽管我一直在将LINQ与XML和展开对象在本文中,这两种方法不是“竞争对手”。你可以转换展开对象X元素反之亦然。例如,这是展开对象X元素转换可能看起来像。

专用静电X元素扩展到XML(动态节点,字符串节点名称){X元素xmlNode(xml节点)=新的X元素(节点名称);foreach公司(无功功率,无功功率财产在里面(IDictionary(图片)<字符串,对象>)节点){如果(属性.Value.GetType()==类型(展开对象))xmlNode。添加(expandoToXML(属性.Value,属性.Key));其他的如果(属性.Value.GetType()==类型(列表<动态>))foreach公司(无功功率,无功功率要素在里面(列表<动态>)属性。价值)xmlNode。添加(expandoToXML(元素,属性.Key));其他的xmlNode。添加(新的X元素(属性.Key,属性.Value);}返回xmlNode;}

这个小技巧可以帮助您在需要时访问所有LINQ to XML函数,但同时在创建和修改XML树时使用更方便的语法。

当然,XML并不是唯一可以使用的领域展开对象。如果大量使用反射或大量处理脚本对象,可以使用展开对象另一方面,展开对象并不是DLR提供的唯一有用的类。这个动态对象例如,类使您能够对动态操作进行更多控制,并定义访问成员或调用方法时实际发生的情况。但这是另一篇博客文章的主题。

还有一点需要注意的是,按名称查找成员的库可能有一天会采用DLR并实现IDynamicMetaObjectProvider接口。(此接口实际上为展开对象以及一般的动态特性。)例如,如果LINQ to XML实现了这个接口,您将能够编写动态联系人=新的XmlElement()而不是动态联系人=新的ExpandoObject()并执行我在示例中为展开对象类型。

本文中提供的所有示例都适用于Visual Studio 2010 Beta 1版。如果您有任何意见或建议,欢迎在此处发布或联系DLR团队网址:http://www.codeplex.com/dlr。您也可以通过以下地址向DLR团队发送电子邮件:dlr@microsoft.com.

更新:

请在我的下一篇帖子中查看如何改进此示例:C#4.0中的动态:使用DynamicObject创建包装.

1条评论

讨论结束。登录以编辑/删除现有评论。

  • 萨姆·克兰默 0

    我同意,这是一篇写得很好的文章,也很有帮助。<a href=“https://www.thetenthub.com网站“>帐篷中心</a>

反馈usabilla图标