在以前的帖子我展示了如何在运行时使用新的dynamic功能和ExpandoObject类来添加和删除属性,这可以使你的代码比使用LINQ代码写入XML语法更具可读性和灵活性。
但也有一些明显的缺陷在例子中:虽然ExpandoObject提供更好的语法,LINQ to XML的图书馆提供了有益的方法,帮助您使用XML文件很多。因此,有可能是这两个优势结合起来,有更好的语法,仍然可以得到所有这些方法?答案是肯定的,但是你需要另一种类型的System.Dynamic命名空间:DynamicObject。
该DynamicObject类使您可以获取或设置覆盖像一个成员操作,调用一个方法,或执行任何二进制,一元,或类型转换操作。为了说明问题,让我们创建一个非常简单的对象覆盖了“获取属性”操作,所以,每当你进入一个属性返回一个字符串属性的名称。这个例子没有任何实际价值。
public class SampleObject : DynamicObject { public override bool TryGetMember( GetMemberBinder binder, out object result) { result = binder.Name; return true; } }
正如ExpandoObject,我们必须使用动态关键字来创建这个类的实例。
dynamic obj = new SampleObject(); Console.WriteLine(obj.SampleProperty); //打印"SampleProperty".
让我们看看会发生什么在这个例子上。当您调用obj.SampleProperty,动态语言运行时(DLR)使用的语言粘结剂寻找这一类属性在SampleObject静态定义。如果没有这样的属性,调用TryGetMember DLR的方法。这种方法得到的信息是什么财产要求通过粘结剂参数。正如你所看到的,binder.Name包含属性的实际名称。
该TryGetMember方法返回true,如果操作成功。但是,实际经营结果必须被分配到out参数的结果。在这个例子中,TryGetMember返回true,但obj.SampleProperty返回“SampleProperty”。
现在让我们转向更为复杂的例子,并创造一个XElement对象包装。再次,我会尽量提供下列LINQ到XML示例更好的语法。
XElement contactXML = new XElement("Contact", new XElement("Name", "Patrick Hines"), new XElement("Phone", "206-555-0144"), new XElement("Address", new XElement("Street1", "123 Main St"), new XElement("City", "Mercer Island"), new XElement("State", "WA"), new XElement("Postal", "68042") ) );
首先,我要创造一个ExpandoObject模拟。我仍然希望能够动态地添加和删除属性。但由于我基本上是创造一个有利于XElement类型的包装,我将使用XElement而不是字典保持性能。
public class DynamicXMLNode : DynamicObject { XElement node; public DynamicXMLNode(XElement node) { this.node = node; } public DynamicXMLNode() { } public DynamicXMLNode(String name) { node = new XElement(name); } public override bool TrySetMember( SetMemberBinder binder, object value) { XElement setNode = node.Element(binder.Name); if (setNode != null) setNode.SetValue(value); else { if (value.GetType() == typeof(DynamicXMLNode)) node.Add(new XElement(binder.Name)); else node.Add(new XElement(binder.Name, value)); } return true; } public override bool TryGetMember( GetMemberBinder binder, out object result) { XElement getNode = node.Element(binder.Name); if (getNode != null) { result = new DynamicXMLNode(getNode); return true; } else { result = null; return false; } } }
这里是你如何可以使用这个类。
dynamic contact = new DynamicXMLNode("Contacts"); contact.Name = "Patrick Hines"; contact.Phone = "206-555-0144"; contact.Address = new DynamicXMLNode(); contact.Address.Street = "123 Main St"; contact.Address.City = "Mercer Island"; contact.Address.State = "WA"; contact.Address.Postal = "68402";
让我们看看在接触对象。当这个对象被创建时,它会初始化内部的XElement。如果您设置一个属性值,在contact.Phone =“206-555-0144喜欢”,该TrySetMember方法检查是否具有名称电话元素在其XElement存在。如果它不存在,该方法创建的元素。
下一个有趣的路线是contact.Address =新DynamicXMLNode()。基本上,在这个特殊的例子,我此行就是要创建一个节点有子节点。对于此属性,TrySetMember方法创建一个XElement没有值。
最复杂的案件在这里是一个如contact.Address.State行=“娃”。首先,TryGetMember方法要求contact.Address并返回一个新的DynamicXMLNode对象,它是由具有名称地址XElement初始化。 (理论上,我可以回到自己的XElement,但是这将让这个例子更加复杂化。)然后TrySetMember方法被调用。该方法查找在contact.Address国的因素。如果它没有找到一个,它创建它。
因此,我已成功地创建了所需的XML结构。但TryGetMember总是返回一个DynamicXMLNode实例。我如何获取XML节点的实际价值?举例来说,我希望下面的行工作,但现在它抛出一个异常。
弦乐状态= contact.Address.State,我这里有几种选择。我可以修改TryGetMember方法返回实际值的叶节点,例如。但是,让我们探讨一个选项:覆盖类型转换。只需将以下方法添加到DynamicXMLNode类。
public override bool TryConvert( ConvertBinder binder, out object result) { if (binder.Type == typeof(String)) { result = node.Value; return true; } else { result = null; return false; } }
现在,每当我有一个明确的DynamicXMLNode式或隐式类型转换,TryConvert方法被调用。该方法检查的对象是什么类型转换为,如果该类型是String,该方法返回的内部XElement价值。否则,它返回false,这意味着该语言应确定下一步该怎么做在大多数情况下(这意味着你将得到一个运行时异常)。
最后一点我要说明的是如何让访问XElement方法。让我们重写TryInvokeMember方法,以便将重定向所有方法调用它的XElement对象。当然,我这里使用System.Reflection命名空间。
public override bool TryInvokeMember( InvokeMemberBinder binder, object[] args, out object result) { Type xmlType = typeof(XElement); try { result = xmlType.InvokeMember( binder.Name, BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, node, args); return true; } catch { result = null; return false; } }
此方法使您能够调用任何DynamicXMLNode对象的节点XElement方法。最明显的缺点,这里是智能感知的情况下。
我什至会假装,这个例子是对LINQ的准备使用的包装,以XML库。它不支持属性,不允许你创建一个节点集合(例如,多个联系人),它可能是失踪的其他功能。创建一个库是一项艰巨的任务,并创造一个良好的包装太。但我希望在阅读这个博客帖子你可以自己创建一个DynamicObject充分运作的包装。
所以,如果你经常使用复杂的语法与检索的XML脚本文件或对象,或者如果您是自己建立这样一个库工程库,你应该考虑写一个包装。这样做可能让你更有效率和您的图书馆语法好得多。
以上程序都在Visual Studio 2010 Beta 2中调试通过。