C#動靜結(jié)合編程中的Duck Typing方法
引言
C#是靜態(tài)類型語言,要使用類型必須引用該類型的定義。因此,從軟件組織角度會發(fā)生組件間的引用依賴關(guān)系。常見的引用依賴關(guān)系有兩種模式:
a. 正向依賴:組件A用到了組件B中定義的類T,組件A直接引用組件B,依賴關(guān)系是“組件A -> 組件B”。
b. 反向依賴:組件A通過接口I定義功能規(guī)范,針對抽象編程;組件B反過來引用組件A,并定義類T實現(xiàn)接口I;由另一組件C將I與T粘合起來,依賴關(guān)系是“組件A <- 組件B”。這就是著名的IoC方式。
簡單說來,IoC是“誰制定規(guī)范,誰就擁有控制權(quán);誰執(zhí)行規(guī)范,誰就被控制”。如果規(guī)范借助于C#的靜態(tài)類型檢查,比如接口或抽象類,那么規(guī)范就表現(xiàn)出較強的語法約束性,使得組件A的編寫比較獨立,而組件B則受制與組件A。
本系列的第一篇舉了一個基于接口的IoC例子,我們看到當(dāng)需要采用第三方組件時,為了適用接口的靜態(tài)類型約束,不得不增加一個adapter去實現(xiàn)接口并包裝對第三方組件的調(diào)用。這表現(xiàn)出基于接口的IoC在粘合規(guī)范與實現(xiàn)時不太靈活。
但是,規(guī)范和類型約束沒有必然的聯(lián)系。在基于委托的IoC例子中,我們不需要任何的adapter,就能輕松的粘合規(guī)范與實現(xiàn),表現(xiàn)出較強的靈活性。這就是通過委托定義規(guī)范,不會造成組件B對組件A的依賴,組件A和組件B的實現(xiàn)都顯得比較獨立。
實際上,我們還可以有比委托更靈活的規(guī)范表達方式,比如:通過HTTP + XML來表達規(guī)范,這樣甚至是語言無關(guān)的,完全可能組件A由C#編寫,組件B由Java編寫。
上面列舉的3種規(guī)范定義方式:基于接口、基于委托、基于HTTP + XML分別代表了由約束到協(xié)議,由嚴(yán)格到靈活的3種風(fēng)格。當(dāng)然,還有更多的方式,但這里只列舉這三種作為代表。動與靜之間需要把握一個分寸,接口過于死板;而HTTP + XML的方式則完全是基于運行時協(xié)議的,需要自己做很多檢查工作;委托的好處在于既消除了組件A、B的依賴關(guān)系,又能享受IDE智能提示和編譯器檢查(簽名檢查)等好處。因此,委托是把動與靜結(jié)合得恰到好處的中庸之道。
Duck Typing
但可惜委托還無法覆蓋接口或類的所有功能,有朋友提到“接口是對象功能的抽象,而委托是方法功能的抽象”就是這個意思。那么我們自然會想,有沒有一種方式,能將委托的思想應(yīng)用于對象呢?有!它就是:duck typing。前文已經(jīng)談到,duck typing關(guān)注“對象能做什么”或者說“如何使用對象”,對象繼承自什么類,或者實現(xiàn)什么接口并不重要。duck typing的本意為“如果一只動物,走起來像鴨子,叫起來像鴨子,我就可以把它當(dāng)作鴨子”。與繼承性多態(tài)相對應(yīng),duck typing可以實現(xiàn)非繼承多態(tài)。按duck typing的本意,純正的duck typing看起來應(yīng)該是這個樣子:
static void Main(string[] args) class Person |
上面的例子中,雖然person對象沒有實現(xiàn)IPerson接口,我們一樣可以通過Duck.Create
C#中要實現(xiàn)Duck.Create
動態(tài)類型
事實上,duck typing是動態(tài)類型概念的一種。C#4.0已經(jīng)通過dynamic關(guān)鍵字來實現(xiàn)動態(tài)類型,讓我們先來看看下面的示例:
string json = @"{ ""FirstName"": ""John"", ""LastName"": ""Smith"", ""Age"": 21 }"; |
通過dynamic關(guān)鍵字,我們不需要在編譯時為person對象指定類型,編譯器不會進行類型檢查,而是將對象的屬性訪問和方法調(diào)用轉(zhuǎn)換為反射調(diào)用,所以,只要對象的運行時類型能通過反射找到匹配的屬性或方法即可。
上面的例子通過json創(chuàng)建了一個dynamic對象,就像javascript中操作json一樣方便。在運行 時,person.FirstName和person.Age能通過反射正確地進行屬性訪問,person.ToJson()也可以正確地執(zhí)行,但 person.Play( "basketball")由于運行時類型不存在該方法而拋出異常。
C#4.0的味道如何?很爽嗎?不過,說實在的,我覺得有點兒不太舒服了!仔細(xì)想想,它像接口,像委托,還是更像HTTP + XML? 對于dynamic對象,編譯器不進行對象類型檢查,不進行屬性類型檢查,也不進行方法簽名檢查。很明顯,它像HTTP+XML,完全基于運行時協(xié)議,沒有一點兒靜態(tài)的東西。如果類比委托的話,更理想的方式應(yīng)該是,不進行對象類型檢查,但進行屬性類型和方法簽名檢查,就像下面這樣:
string json = @"{ ""FirstName"": ""John"", ""LastName"": ""Smith"", ""Age"": 21 }"; |
這樣,除了屬性和方法的名稱是動態(tài)的外,屬性的類型和方法的簽名都是靜態(tài)的,把運行時錯誤的可能降到最低,同時享受靜態(tài)檢查的好處。其實,沿著這個思路,我們大可不必等著C#4.0的dynamic才開始動態(tài)類型,在C#2.0時代也可以這樣:
object jsonObj = CreateFromJson(@"{ ""FirstName"": ""John"", ""LastName"": ""Smith"", ""Age"": 21 }"); |
看到這里,相信您一定明白該如何實現(xiàn)Dynamic類了吧?如果覺得有用,就自己嘗試實現(xiàn)一下吧!
博文鏈接:http://www.cnblogs.com/weidagang2046/archive/2009/03/26/1421943.html
【編輯推薦】