二十行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.new xml = 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類只能算是冰山一角而已。
原文地址:http://www.cnblogs.com/JeffreyZhao/archive/2009/10/27/1590321.html
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/12639172/viewspace-617530/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- rails on ruby,ruby on rails 之程式碼塊(二)AI
- 十行程式碼實現牛頓方法行程
- 十行程式碼實現高仿Promise行程Promise
- 十行程式碼帶你量化交易入門行程
- 十行pyhon程式碼批量更換icon顏色
- SonarQube系列二、分析dotnet core/C#程式碼C#
- Ruby 中的閉包-程式碼塊
- C++Builder常用程式碼片斷 (轉)C++UI
- [-Flutter自定義元件-] Flutter中四十行程式碼能做什麼?Flutter元件行程
- 將 SQL轉換成 Laravel Query Builder 程式碼SQLLaravelUI
- 十行程式碼實現網頁標題滾動效果!行程網頁
- 六十行程式碼完成 四則運算 語法解析器行程
- 十行 Python 程式碼就提取了韋小寶的身份證資訊!Python
- 十行Python程式碼搞定圖片中的物體檢測Python
- 10個驚豔的Ruby單行程式碼行程
- 程式語言擬人化:Java、C++、Python、Ruby、PHP、C#、JSJavaC++PythonPHPC#JS
- 幾十行程式碼實現一個vue的狀態管理行程Vue
- 前端教程分享:十行程式碼實現title滾動顯示前端行程
- 三十行程式碼教你批量爬取某網站妹紙圖行程網站
- Flutter編譯時生成程式碼之 code_builderFlutter編譯UI
- C#設計模式-建造者模式(Builder Pattern)C#設計模式UI
- C#設計模式系列:建造者模式(Builder)C#設計模式UI
- 關於檔案複製的程式程式碼(C++Builder) (轉)C++UI
- DSL-讓你的 Ruby 程式碼更加優雅
- RubyMine 2022 Mac(Ruby程式碼編輯)Mac
- 程式語言擬人化(1):Java、C++、Python、Ruby、PHP、C#、JSJavaC++PythonPHPC#JS
- 用幾十行程式碼實現python中英文分詞行程Python分詞
- c# 程式設計學習(二)C#程式設計
- 【設計模式筆記】(二)- Builder模式設計模式筆記UI
- yaml (YAML Ain't Markup Language)YAMLAI
- 微信小程式:小程式碼、小程式二維碼、普通二維碼微信小程式
- C#入門程式碼C#
- [C#] 程式碼規範C#
- c# 智慧升級程式程式碼C#
- Ruby程式語言與Ruby之間的比較
- 幾十行程式碼實現ASP.NET Core自動依賴注入行程ASP.NET依賴注入
- 所謂的演算法,有時候幾十行程式碼就能搞定!演算法行程
- 50行ruby程式碼開發一個區塊鏈區塊鏈