项目中很多地方采用dynamic新特性进行开发,因此本人参看了很多关于dynamic的资料。在新的一年开始,记录下自己的学习经验。
dynamic关键字用于声明一个动态对象,然后通过该动态对象去调用方法或读写属性。
(一)测试Alexandra Rusina提供的参考资料
参考http://blogs.msdn.com/b/csharpfaq/archive/2009/10/19/dynamic-in-c-4-0-creating-wrappers-with-dynamicobject.aspx ,因此对该地址的博文中DynamicXMLNode类进行相应的学习与测试。测试如下:
2 {
3 //正常运行
4 RunDynamicXMLNode(true);
5 //异常运行
6 RunDynamicXMLNode(false);
7
8 Console.ReadLine();
9 }
2 {
3 Console.WriteLine("------RUN begin------");
4 try
5 {
6 dynamic contact = new DynamicXMLNode("Contacts");
7 contact.Name = "Patrick Hines";
8 if (state)
9 {
10 contact.Address = new DynamicXMLNode();
11 }
12 contact.Address.Street = "123 Main St";
13 }
14 catch (RuntimeBinderException ex)
15 {
16 Console.WriteLine(ex.Message);
17 }
18 Console.WriteLine("------RUN end--------");
19 }
运行后显示结果如下:
设置断点进行单步调试,发现TryGetMember方法中:如果当前元素XElement下没有相应的XName的子元素,那么result = null;
正常运行contact.Address = new DynamicXMLNode();创建了一个新的dynamic对象。反之,contact.Address =null;将导致RuntimeBinderException异常。
2 {
3 XElement getNode = node.Element(binder.Name);
4 if (getNode != null)
5 {
6 result = new DynamicXMLNode(getNode);
7 return true;
8 }
9 else
10 {
11 result = null;
12 return false;
13 }
14 }
(二)对以上的类进行相应的修改
因此本人将之进行相应的改进,result = new DynamicXElement(binder.Name); 当无法找到匹配的XElement时,返回一个新的DynamicXElement对象。改动如下:
2 GetMemberBinder binder, out object result)
3 {
4 XElement getNode = this.XContent.Element(binder.Name);
5 if (getNode != null)
6 {
7 result = new DynamicXElement(getNode);
8 }
9 else
10 {
11 result = new DynamicXElement(binder.Name);
12 }
13 return true;
14 }
2 {
3 Console.WriteLine("----RunDynamicXElement begin-----");
4 dynamic contact = new DynamicXElement("Contacts");
5 contact.Name = "Patrick Hines";
6 contact.Address.Street = "123 Main St";
7 Console.WriteLine("----RunDynamicXElement end-----");
8 }
因此,contact.Address.Street = "123 Main St";这句将会创建一个新的DynamicXElement对象,参数为"Address",此处将不再抛出RuntimeBinderException异常。
(三)定义一个DynamicHelper类,实现dynamic对象与XML之间的互换操作
自定义一个DynamicHelper类,主要是实现dynamic对象与XML之间的互换操作。
ToXml(dynamic dynamicObject)将传入的dynamicObject转换为xml。
ToObject(string xml, dynamic dynamicResult) 将传入的string,先转换成XElement元素,然后再构建一个DynamicXElement对象,返回结果。
ToObject(string xml)直接将xml转换为DynamicXElement对象。
2 {
3 public static string ToXml(dynamic dynamicObject)
4 {
5 DynamicXElement xmlNode = dynamicObject;
6 return xmlNode.XContent.ToString();
7 }
8
9 public static dynamic ToObject(string xml, dynamic dynamicResult)
10 {
11 XElement element = XElement.Parse(xml);
12 dynamicResult = new DynamicXElement(element);
13 return dynamicResult;
14 }
15
16 public static dynamic ToObject(string xml)
17 {
18 XElement element = XElement.Parse(xml);
19 dynamic dynamicResult = new DynamicXElement(element);
20 return dynamicResult;
21 }
22 }
为了实现ToXml()方法,修改DynamicXElement类,将私有变量node设置为属性XContent(并且设置set 方法为私有的)。这里,主要是为了获取XElement的所有内容而改进的。设置后的DynamicXElement类如下:
2 {
3 public DynamicXElement(XElement node)
4 {
5 this.XContent = node;
6 }
7
8 public DynamicXElement()
9 {
10 }
11
12 public DynamicXElement(String name)
13 {
14 this.XContent = new XElement(name);
15 }
16
17 public XElement XContent
18 {
19 get;
20 private set;
21 }
22
23 public override bool TrySetMember(
24 SetMemberBinder binder, object value)
25 {
26 XElement setNode = this.XContent.Element(binder.Name);
27 if (setNode != null)
28 setNode.SetValue(value);
29 else
30 {
31 //creates an XElement without a value.
32 if (value.GetType() == typeof(DynamicXElement))
33 this.XContent.Add(new XElement(binder.Name));
34 else
35 this.XContent.Add(new XElement(binder.Name, value));
36 }
37 return true;
38 }
39
40 public override bool TryGetMember(
41 GetMemberBinder binder, out object result)
42 {
43 XElement getNode = this.XContent.Element(binder.Name);
44 if (getNode != null)
45 {
46 result = new DynamicXElement(getNode);
47 }
48 else
49 {
50 result = new DynamicXElement(binder.Name);
51 }
52 return true;
53 }
54
55 public override bool TryConvert(
56 ConvertBinder binder, out object result)
57 {
58 if (binder.Type == typeof(String))
59 {
60 result = this.XContent.Value;
61 return true;
62 }
63 else
64 {
65 result = null;
66 return false;
67 }
68 }
69 }
其他的都设置好了以后,RunDynamicHelper()将验证以上的方法,对DynamicHelper中所有的方法进行相应的测试。
2 {
3 //构造一个dynamic对象
4 dynamic dynamicObject = new DynamicXElement("ImportSample");
5
6 //设置XML
7 string xml = Resources.ImportSample;
8
9 //转换成为dynamic实体(赋值) ToObject()
10 dynamic dynamicResult = DynamicHelper.ToObject(xml, dynamicObject);
11
12 string message = dynamicResult.Import.Message;
13 Console.WriteLine(message);
14 Console.WriteLine();
15
16 //转换成为dynamic实体(赋值) ToObject()
17 dynamic dynamicResult2 = DynamicHelper.ToObject(xml);
18
19 message = dynamicResult2.Import.Message;
20 Console.WriteLine(message);
21 Console.WriteLine();
22 //ToXml()
23 string xmlResult = DynamicHelper.ToXml(dynamicResult);
24 Console.WriteLine(xmlResult);
25 }
显示结果如下:
(四)注意:对于我们添加的资源文件,需要对自动生成的.cs进行相应的修改。
资源文件自动添加之后,类以及相关的属性访问修饰符将默认设置为internal(程序集可见),因此我们必须对访问修饰符进行相应的修改,设置为public。
2 [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
3 internal Resources() {
4 }
5 public static string ImportSample {
6 get {
7 return ResourceManager.GetString("ImportSample", resourceCulture);
8 }
9 }
参考文献如下:
http://blogs.msdn.com/b/csharpfaq/archive/tags/dlr/
http://blog.zhaojie.me/tag/dynamic/
http://blog.zhaojie.me/2009/10/implement-ruby-markup-builder-in-20-lines-of-c-sharp-codes.html
http://blog.zhaojie.me/2010/05/generate-dynamic-method-with-expression-tree-in-dot-net-4.html
源代码下载地址:Dynamic与xml的相互转换源代码