二十行C#代码打造Ruby Markup Builder

  从.NET诞生之日起就有了XML类库,但是从使用上来说非常不方便。例如我们需要构造一个XML文档时,使用DOM API就要这样搞:

var xmlDoc = new XmlDocument();var rootEle = xmlDoc.CreateElement("persons");xmlDoc.AppendChild(rootEle);var person1 = xmlDoc.CreateElement("person");person1.InnerText = "Tom";var person1Age = xmlDoc.CreateAttribute("age");person1Age.Value = "10";person1.Attributes.Append(person1Age);rootEle.AppendChild(person1);var person2 = xmlDoc.CreateElement("person");person2.InnerText = "Jerry";var person2Age = xmlDoc.CreateAttribute("age");person2Age.Value = "8";person2.Attributes.Append(person2Age);rootEle.AppendChild(person2);

  别看这么多行代码,但实际上它只构造了这么简单的一个XML:

<persons> <person age="10">Tomperson> <person age="8">Jerryperson>persons>

  我承认,DOM API的确非常严谨(如XmlDocument和XmlElement的归属关系),非常符合定义,也非常的面向对象,但是这易用性也实在太差了。记得在03还是04年的时候,我为在为项目做一个编辑XML文档的WinForm应用程序,当时也不像现在那么容易想到“偷懒”的法门,而VS 2003也不像VS 2005/2008那么好用,因此可谓做的劳心费神。这个情况在.NET 2.0中也没有得到改变,直到有一天,LINQ to XML随.NET 3.5横空出世,于是乎XML的生活一下子变得美好了很多。例如上面的功能只需寥寥数行便可以实现:

var xmlDoc = new XElement("persons", new XElement("person", "Tom", new XAttribute("age", 10)), new XElement("person", "Jerry", new XAttribute("age", 8)));

  虽然LINQ to XML一直是所谓C# 3.0中LINQ特性的一部分,与LINQ to SQL,LINQ to Object及LINQ to……某个别的并列,但我始终认为LINQ to XML实则还是LINQ to Object的一种特殊形式,只是它用于操作XML而已。它的一切都是System.Xml.Linq命名空间下相关类库(如XElement)在起作用,不关LINQ什么事情。XElement等相关类型大大简化了我们的开发,与DOM API相比,无论是XML的构造还是读取都容易了许多。不过俗话说得好:“不怕不识货,就怕货比货”,这样的API与Ruby Markup Builder相比还是有明显差距。请看:

builder = Builder::XmlMarkup.newxml = builder.persons { |b| b.person("Tom", :age => "10") b.person("Jerry", :age => "8")}

  请看上面这段代码,它自然没有使用Ruby语言的标准着色方式。我着色的目的是体现这个构造方式中的“噪音”——也就是与XML内容无关的部分。从中可以发现,Ruby不愧是一种噪音较少的语言,如果您尝试使用这个方式来观察C#中LINQ to XML的做法,就会发现两者之间的确有明显的差距。当然,如果使用VB.NET的XML Literal可能噪音也很少,但是在我看来,XML Literal在XML构造方面的表现有些罗嗦,例如它需要开发人员同时提供元素的开始标签和闭合标签,可能在IDE的帮助下此类代码输入较为简单,但是代码还是略显冗余。

  但是我们这些可怜的C#程序员难道只有在一边眼馋的份吗?不见得,我们也可以来“享受”一把:

dynamic b = new XmlMarkupBuilder();XElement xml = b.persons( b.person("Tom", age: 10), b.person("Jerry", age: 8));

  哇,这是什么,怎么代码那么简单。很明显,从dynamic关键字上可以看出,这是C# 4.0中新增的功能。您可能会想“原来.NET 4.0对XML又有增强了”……其实并非如此,这是我们自己扩展的功能。不过这应该算是更好的消息,因为这说明我们已经有能力自行扩展,自行设计这样的API了——这可是“渔”,比“鱼”可要值钱多了。而实现这样的功能也只需要短短二十几行C#代码:

public class XmlMarkupBuilder : DynamicObject{ public override bool TryInvokeMember(InvokeMemberBinder binder,

object[] args, out object result) { XElement xml = new XElement(binder.Name); var attrCount = binder.CallInfo.ArgumentNames.Count; var elementCount = args.Length - attrCount; for (int i = 0; i < elementCount; i++) { xml.Add(args[i]); } for (var i = 0; i < attrCount; i++) { var attrName = binder.CallInfo.ArgumentNames[i]; if (attrName[0] == '@') attrName = attrName.Substring(1); xml.Add(new XAttribute(attrName, args[i + elementCount])); } result = xml; return true; }}

  DynamicObject是个特殊的对象,简单地说它的行为可以被“扩展”——是如动态语言般真正的扩展,而非静态的多态。当我们使用dynamic修饰变量后,在它之上的方法调用会由编译器和DLR配合出不一样的行为。例如,我们在调用一个方法的时候,DLR会先检查这个动态对象上是否存在符合这个签名的方法,存在则最好,否则便会调用TryInvokeMember来“执行”一个动态方法,而它的参数便是此次调用的全部信息。这样的做法被称为“Method Missing”操作,事实上Ruby Markup Builder也是使用Ruby对象中的这个特性来实现“调用什么方法,便生成什么元素”的功能。事实上,我们还可以这么用:

var persons = new [] { new Person("Tom", 10), new Person("Jerry", 8) };XElement xml2 = b.persons( from p in persons select b.person(p.Name, age: p.Age));

  XmlMarkupBuilder对LINQ的直接支持得益于XElement无与伦比的“包容性”(因此我认为LINQ to XML其实只是LINQ to Object + 类库)。至于age: 10这样的代码,其实是使用了C# 4.0的新特性:命名参数(Named Parameters)——C#还真把什么都为我们准备好了。

  即便是大部分DynamicObject的示例都喜欢拿XML操作开涮(但还是没有出现我这篇的用法,所以我还是“原创”),但事实上这个功能可发挥的余地非常之大。例如,陈猫同学提到他想用这个功能来简化Silverlight中的JSON操作,刚“喜得贵女”的Phil Haack同学在上个月也提到一个设想,它在ASP.NET MVC中使用dynamic关键字来修饰View的Model,这样在访问Model的属性时变可附加一些约定好的操作。例如,Model.Content表示读取Content属性的内容,而Model._Content则表示在读取Content之后自动进行HTML编码。这无疑简化了我们的开发——当然,强类型的各种优势就不复存在了。

  而这个功能对我的意义在于,我又找到了一种设计API的方式,它可以使类库变得简单好用——就好比上面的XmlMarkupBuilder一样。虽然,这个示例的功能非常简单,但是这也足以证明C# 4.0中的dynamic特性并不仅仅是“方便Interop操作”或是“简化反射”这么简单,如果我们可以发挥想象能力,加以充分利用同时又不滥用,我们的程序开发生活就会变得越来越美好。

  最后……我还是承认了吧,这篇文章其实是标题党,真正Ruby Markup Builder功能非常强大而复杂,我们的XmlMarkupBuilder类只能算是冰山一角而已。

时间: 2024-10-14 19:13:53

二十行C#代码打造Ruby Markup Builder的相关文章

艾伟_转载:二十行C#代码打造Ruby Markup Builder

从.NET诞生之日起就有了XML类库,但是从使用上来说非常不方便.例如我们需要构造一个XML文档时,使用DOM API就要这样搞: var xmlDoc = new XmlDocument(); var rootEle = xmlDoc.CreateElement("persons"); xmlDoc.AppendChild(rootEle); var person1 = xmlDoc.CreateElement("person"); person1.InnerTe

用Ruby+Builder创建XML应用

一. 简介 自从去年Ruby on Rails问世后,人们对Ruby编程语言的兴趣似乎稳定地增长起来.Rails已经帮助人 们看到了什么是Ruby:一种非常体面的,易学的且使用中充满了乐趣的语言,甚至适合于工业级应用软件 的开发.Ruby出现在几乎与Java差不多的年代,但是直到如今它才引起人们的关注. Ruby当然也引起我的注意!最近,我发现自己使用Ruby的时间多于Java了,因为它可以帮助我更快地 编码-实现相同的工作,但是击键次数却更少.我简直怀疑我是否实际完全放弃了Java而转向了Ru

asp.net C#生成和解析二维码代码

  类库文件我们在文件最后面下载 [ThoughtWorks.QRCode.dll 就是类库] 使用时需要增加: using ThoughtWorks.QRCode.Codec; using ThoughtWorks.QRCode.Codec.Data; using ThoughtWorks.QRCode.Codec.Util; 主要源代码: 1.生成二维码 代码如下   QRCodeEncoder qrCodeEncoder = new QRCodeEncoder(); String enco

jquery读取json文件二款实例代码(1/2)

文章为你免费提供二款jquery读取网页特效on文件二款实例代码,这些代码非常实用哦 json文件: [ { "name":"xujun", "sex":"男", "home":"nanjing" }, { "name":"jack", "sex":"男", "home":"be

ASP 三十二条精华代码

欢迎您访问我在博客园上的博客,上面有更多相关技术文章.我的博客地址是:http://www.cnblogs.com/scq2099yt. 欢迎您访问我在百度上的博客,上面有更多相关技术文章.我的博客地址是:http://hi.baidu.com/scq2099yt. 整理收藏: ASP 三十二条精华代码   1. oncontextmenu="window.event.returnvalue=false" 将彻底屏蔽鼠标右键 <table border oncontextmenu

asp.net中利用QRCode生成二维码代码

1.使用下面代码首先需要下载QRCode.DLL文件引用到你的项目中  代码如下 复制代码 //生成二维码代码 public string generateQRCode() {     //生成二维码     string filename = string.Empty;     string filepath = string.Empty;       string txt_qr = "需要生成二维码信息";     string qrEncoding = "Byte&qu

对三维实体进行曲面剖切操作,获得二维三视图代码

问题描述 对三维实体进行曲面剖切操作,获得二维三视图代码

php Mysqli预处理语句二款实例代码

php教程 mysql教程i预处理语句二款实例代码 <?php /*=========================mysqli_stmt预处理类(推荐使用)=========================*/ /*===============================优点:效率高,安全================================*/  $mysqli=new mysqli("localhost","root","12

新手学Linux(二)----使用 Vagrant 打造跨平台开发环境(一)

前言 什么是Vagrant Vagrant能做什么 尽可能避免Work on my machine错误 缩短搭建开发环境的时间 Vagrant的主要使用者 前言     做Web开发少不了要在本地搭建好开发环境,虽然说目前各种脚本都有对应的Windows版,甚至是一键安装包,但很多时候和Windows环境的相性并不是那么好,各麻烦的问题是实际部署的环境通常是Linux,常常还要面临着开发和部署环境不一致,上线前还要大量的调试.更要命的是,如果有很多机器需要装的话,那就真是一个灾难了. 什么是Va