新聞中心
Ruby語(yǔ)法的簡(jiǎn)潔深為程序員們所喜愛,而寫C#的往往要忍受易用性很差的寫法。不過(guò)由于.NET 4.0的到來(lái),C# 4.0新增的dynamic功能開放了更多的自己設(shè)計(jì)API的選擇,這樣就開啟了很多自由的編程道路。老趙在下面介紹了自己打造的一個(gè)Ruby Markup Builder,展示了其強(qiáng)大的XML構(gòu)造功能。

創(chuàng)新互聯(lián)建站服務(wù)項(xiàng)目包括奎屯網(wǎng)站建設(shè)、奎屯網(wǎng)站制作、奎屯網(wǎng)頁(yè)制作以及奎屯網(wǎng)絡(luò)營(yíng)銷策劃等。多年來(lái),我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,奎屯網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到奎屯省份的部分城市,未來(lái)相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!
從.NET誕生之日起就有了XML類庫(kù),但是從使用上來(lái)說(shuō)非常不方便。例如我們需要構(gòu)造一個(gè)XML文檔時(shí),使用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);
別看這么多行代碼,但實(shí)際上它只構(gòu)造了這么簡(jiǎn)單的一個(gè)XML:
- <persons>
- <person age="10">Tom</person>
- <person age="8">Jerry</person>
- </persons>
我承認(rèn),DOM API的確非常嚴(yán)謹(jǐn)(如XmlDocument和XmlElement的歸屬關(guān)系),非常符合定義,也非常的面向?qū)ο?,但是這易用性也實(shí)在太差了。記得在03還是04年的時(shí)候,我為在為項(xiàng)目做一個(gè)編輯XML文檔的WinForm應(yīng)用程序,當(dāng)時(shí)也不像現(xiàn)在那么容易想到“偷懶”的法門,而VS 2003也不像VS 2005/2008那么好用,因此可謂做的勞心費(fèi)神。這個(gè)情況在.NET 2.0中也沒有得到改變,直到有一天,LINQ to XML隨.NET 3.5橫空出世,于是乎XML的生活一下子變得美好了很多。例如上面的功能只需寥寥數(shù)行便可以實(shí)現(xiàn):
- 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……某個(gè)別的并列,但我始終認(rèn)為L(zhǎng)INQ to XML實(shí)則還是LINQ to Object的一種特殊形式,只是它用于操作XML而已。它的一切都是System.Xml.Linq命名空間下相關(guān)類庫(kù)(如XElement)在起作用,不關(guān)LINQ什么事情。XElement等相關(guān)類型大大簡(jiǎn)化了我們的開發(fā),與DOM API相比,無(wú)論是XML的構(gòu)造還是讀取都容易了許多。不過(guò)俗話說(shuō)得好:“不怕不識(shí)貨,就怕貨比貨”,這樣的API與Ruby Markup Builder相比還是有明顯差距。請(qǐng)看:
- builder = Builder::XmlMarkup.new
- xml = builder.persons { |b|
- b.person("Tom", :age => "10")
- b.person("Jerry", :age => "8")
- }
請(qǐng)看上面這段代碼,它自然沒有使用Ruby語(yǔ)言的標(biāo)準(zhǔn)著色方式。我著色的目的是體現(xiàn)這個(gè)構(gòu)造方式中的“噪音”——也就是與XML內(nèi)容無(wú)關(guān)的部分。從中可以發(fā)現(xiàn),Ruby不愧是一種噪音較少的語(yǔ)言,如果您嘗試使用這個(gè)方式來(lái)觀察C#中LINQ to XML的做法,就會(huì)發(fā)現(xiàn)兩者之間的確有明顯的差距。當(dāng)然,如果使用VB.NET的XML Literal可能噪音也很少,但是在我看來(lái),XML Literal在XML構(gòu)造方面的表現(xiàn)有些羅嗦,例如它需要開發(fā)人員同時(shí)提供元素的開始標(biāo)簽和閉合標(biāo)簽,可能在IDE的幫助下此類代碼輸入較為簡(jiǎn)單,但是代碼還是略顯冗余。
但是我們這些可憐的C#程序員難道只有在一邊眼饞的份嗎?不見得,我們也可以來(lái)“享受”一把:
- dynamic b = new XmlMarkupBuilder();
- XElement xml =
- b.persons(
- b.person("Tom", age: 10),
- b.person("Jerry", age: 8));
哇,這是什么,怎么代碼那么簡(jiǎn)單。很明顯,從dynamic關(guān)鍵字上可以看出,這是C# 4.0中新增的功能。您可能會(huì)想“原來(lái).NET 4.0對(duì)XML又有增強(qiáng)了”……其實(shí)并非如此,這是我們自己擴(kuò)展的功能。不過(guò)這應(yīng)該算是更好的消息,因?yàn)檫@說(shuō)明我們已經(jīng)有能力自行擴(kuò)展,自行設(shè)計(jì)這樣的API了——這可是“漁”,比“魚”可要值錢多了。而實(shí)現(xiàn)這樣的功能也只需要短短二十幾行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是個(gè)特殊的對(duì)象,簡(jiǎn)單地說(shuō)它的行為可以被“擴(kuò)展”——是如動(dòng)態(tài)語(yǔ)言般真正的擴(kuò)展,而非靜態(tài)的多態(tài)。當(dāng)我們使用dynamic修飾變量后,在它之上的方法調(diào)用會(huì)由編譯器和DLR配合出不一樣的行為。例如,我們?cè)谡{(diào)用一個(gè)方法的時(shí)候,DLR會(huì)先檢查這個(gè)動(dòng)態(tài)對(duì)象上是否存在符合這個(gè)簽名的方法,存在則最好,否則便會(huì)調(diào)用TryInvokeMember來(lái)“執(zhí)行”一個(gè)動(dòng)態(tài)方法,而它的參數(shù)便是此次調(diào)用的全部信息。這樣的做法被稱為“Method Missing”操作,事實(shí)上Ruby Markup Builder也是使用Ruby對(duì)象中的這個(gè)特性來(lái)實(shí)現(xiàn)“調(diào)用什么方法,便生成什么元素”的功能。事實(shí)上,我們還可以這么用:
- 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對(duì)LINQ的直接支持得益于XElement無(wú)與倫比的“包容性”(因此我認(rèn)為L(zhǎng)INQ to XML其實(shí)只是LINQ to Object + 類庫(kù))。至于age: 10這樣的代碼,其實(shí)是使用了C# 4.0的新特性:命名參數(shù)(Named Parameters)——C#還真把什么都為我們準(zhǔn)備好了。
即便是大部分DynamicObject的示例都喜歡拿XML操作開涮(但還是沒有出現(xiàn)我這篇的用法,所以我還是“原創(chuàng)”),但事實(shí)上這個(gè)功能可發(fā)揮的余地非常之大。例如,陳貓同學(xué)提到他想用這個(gè)功能來(lái)簡(jiǎn)化Silverlight中的JSON操作,剛“喜得貴女”的Phil Haack同學(xué)在上個(gè)月也提到一個(gè)設(shè)想,它在ASP.NET MVC中使用dynamic關(guān)鍵字來(lái)修飾View的Model,這樣在訪問Model的屬性時(shí)變可附加一些約定好的操作。例如,Model.Content表示讀取Content屬性的內(nèi)容,而Model._Content則表示在讀取Content之后自動(dòng)進(jìn)行HTML編碼。這無(wú)疑簡(jiǎn)化了我們的開發(fā)——當(dāng)然,強(qiáng)類型的各種優(yōu)勢(shì)就不復(fù)存在了。
而這個(gè)功能對(duì)我的意義在于,我又找到了一種設(shè)計(jì)API的方式,它可以使類庫(kù)變得簡(jiǎn)單好用——就好比上面的XmlMarkupBuilder一樣。雖然,這個(gè)示例的功能非常簡(jiǎn)單,但是這也足以證明C# 4.0中的dynamic特性并不僅僅是“方便Interop操作”或是“簡(jiǎn)化反射”這么簡(jiǎn)單,如果我們可以發(fā)揮想象能力,加以充分利用同時(shí)又不濫用,我們的程序開發(fā)生活就會(huì)變得越來(lái)越美好。
最后……我還是承認(rèn)了吧,這篇文章其實(shí)是標(biāo)題黨,真正Ruby Markup Builder功能非常強(qiáng)大而復(fù)雜,我們的XmlMarkupBuilder類只能算是冰山一角而已。
【編輯推薦】
- 詳解C# 4.0中的新對(duì)象ExpandoObject
- 介紹C# 4.0新特性dynamic
- C#歷史回顧及C# 4.0新特性一覽
- C# 4.0 Dynamic關(guān)鍵字全解析
- C# 4.0 的4個(gè)新特性
標(biāo)題名稱:增強(qiáng)的XML?通過(guò)C# 4.0 dynamic享受Ruby寫法之探討
網(wǎng)站路徑:http://www.dlmjj.cn/article/cdoppjd.html


咨詢
建站咨詢
