轉(zhuǎn)帖|其它|編輯:郝浩|2010-08-27 11:49:43.000|閱讀 956 次
概述:作為微軟.NET平臺(tái)最重要的支柱,C#一直以一種不斷革新的面貌出現(xiàn),從C#1.0的委托到C# 4的動(dòng)態(tài)編程語(yǔ)言。這也是C#成為微軟鋒利的刀的原因之一。
# 界面/圖表報(bào)表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
近幾年來(lái),在TIOBE公司每個(gè)月發(fā)布的編程語(yǔ)言排行榜[1]中,C#總是能擠進(jìn)前10名,而在近10年的編程語(yǔ)言排行榜中,C#總體上呈現(xiàn)上升的趨勢(shì)。C#能取得這樣的成績(jī),有很多因素在起作用,其中,它在語(yǔ)言特性上的銳意進(jìn)取讓人印象深刻(圖 1)。51CTO向您推薦《8月編程語(yǔ)言排行榜:微軟鋒利的刀C#》
圖 1 C#各版本的創(chuàng)新點(diǎn)
2010年發(fā)布的C# 4,最大的創(chuàng)新點(diǎn)是擁有了動(dòng)態(tài)編程語(yǔ)言的特性。
1 動(dòng)態(tài)編程語(yǔ)言的中興
動(dòng)態(tài)編程語(yǔ)言并非什么新鮮事物,早在面向?qū)ο缶幊陶Z(yǔ)言成為主流之前,人們就已經(jīng)使用動(dòng)態(tài)編程語(yǔ)言來(lái)開(kāi)發(fā)了。即使在Java、C#、C++等面向?qū)ο缶幊陶Z(yǔ)言繁榮興旺、大行于世的年代,動(dòng)態(tài)編程語(yǔ)言也在“悄悄”地攻城掠地,占據(jù)了相當(dāng)?shù)拈_(kāi)發(fā)領(lǐng)域,比如 JavaScript業(yè)已成為Web客戶端事實(shí)上的主流語(yǔ)言。
最近這幾年,動(dòng)態(tài)編程語(yǔ)言變得日益流行,比如Python、Ruby都非常活躍,使用者眾多。
這里有一個(gè)問(wèn)題,為什么我們需要在開(kāi)發(fā)中應(yīng)用動(dòng)態(tài)編程語(yǔ)言?與C#和Java這類已經(jīng)非常成熟且功能強(qiáng)大的靜態(tài)類型編程語(yǔ)言相比,動(dòng)態(tài)編程語(yǔ)言有何優(yōu)勢(shì)?
簡(jiǎn)單地說(shuō),使用動(dòng)態(tài)編程語(yǔ)言開(kāi)發(fā)擁有以下的特性:
(1)支持REPL(Read-evaluate-print Loop:“讀入à執(zhí)行à輸出”循環(huán)迭代)的開(kāi)發(fā)模式,整個(gè)過(guò)程簡(jiǎn)潔明了,直指問(wèn)題的核心。
舉個(gè)簡(jiǎn)單的例子,圖 2所示為使用IronPython[2]編程計(jì)算“1+2+……+100”的屏幕截圖,我們可以快速地輸入一段完成累加求和的代碼,然后馬上就可以看到結(jié)果:
圖 2 使用IronPython編程
如果使用C#開(kāi)發(fā)就麻煩多了,您得先用Visual Studio創(chuàng)建一個(gè)項(xiàng)目,然后向其中添加一個(gè)類,在類中寫(xiě)一個(gè)方法完成求和的功能,再編寫(xiě)調(diào)用這一方法的代碼,編譯、排錯(cuò),最后才能得到所需的結(jié)果……
很明顯,對(duì)于那些短小的工作任務(wù)而言,動(dòng)態(tài)編程語(yǔ)言所具備的這種REPL開(kāi)發(fā)模式具有很大的吸引力。
(2)擴(kuò)展方便。用戶可以隨時(shí)對(duì)代碼進(jìn)行調(diào)整,需要什么功能直接往動(dòng)態(tài)對(duì)象上“加”就是了,不要時(shí)又可以移除它們。而且這種修改可以馬上生效,并不需要像C#那樣必須先修改類型的定義和聲明,編譯之后新方法才可用。
換句話說(shuō):使用動(dòng)態(tài)語(yǔ)言編程,不需要“重量級(jí)”的OOAD,整個(gè)開(kāi)發(fā)過(guò)程迭代迅速而從不拖泥帶水。
(3)動(dòng)態(tài)編程語(yǔ)言的類型解析是在運(yùn)行時(shí)完成的,可以省去許多不必要的類型轉(zhuǎn)換代碼,因此,與靜態(tài)編程語(yǔ)相比,動(dòng)態(tài)編程語(yǔ)言寫(xiě)的代碼往往更緊湊,量更少。
動(dòng)態(tài)編程語(yǔ)言主要的弱點(diǎn)有兩個(gè):
(1)代碼中的許多錯(cuò)誤要等到運(yùn)行時(shí)才能發(fā)現(xiàn),而且需要特定的運(yùn)行環(huán)境支持,對(duì)其進(jìn)行測(cè)試不太方便,也不支持許多用于提升代碼質(zhì)量的各種軟件工程工具,因此不太適合于開(kāi)發(fā)規(guī)模較大的、包容復(fù)雜處理邏輯的應(yīng)用系統(tǒng)。
(2)與靜態(tài)編程語(yǔ)言相比,動(dòng)態(tài)編程語(yǔ)言編寫(xiě)的程序性能較低。不過(guò)隨著計(jì)算機(jī)軟硬件技術(shù)的不斷進(jìn)步,比如多核CPU的廣泛應(yīng)用,動(dòng)態(tài)編程語(yǔ)言引擎和運(yùn)行環(huán)境不斷地優(yōu)化,動(dòng)態(tài)編程語(yǔ)言編寫(xiě)的程序性能在不斷地提升,在特定的應(yīng)用場(chǎng)景下,甚至可以逼近靜態(tài)語(yǔ)言編寫(xiě)的程序。
2 擁抱“動(dòng)態(tài)編程”特性的C# 4
為了讓C#、Visual Basic等.NET編程語(yǔ)言能具備動(dòng)態(tài)編程語(yǔ)言的特性,.NET 4.0引入了一個(gè)“DLR(Dynamic Language Runtime:動(dòng)態(tài)語(yǔ)言運(yùn)行時(shí))”(圖 3)。
圖 3 DLR:動(dòng)態(tài)語(yǔ)言運(yùn)行時(shí)
DLR運(yùn)行于CLR之上,提供了一個(gè)動(dòng)態(tài)語(yǔ)言的運(yùn)行環(huán)境,從而允許Python、Ruby等動(dòng)態(tài)語(yǔ)言編寫(xiě)的程序在.NET平臺(tái)上運(yùn)行,同時(shí),現(xiàn)有的.NET靜態(tài)類型編程語(yǔ)言,比如C#和Visual Basic,也可以利用DLR而擁有一些動(dòng)態(tài)編程語(yǔ)言的特性。
(1)使用C# 4編寫(xiě)動(dòng)態(tài)的代碼
C# 4新增了一個(gè)dynamic關(guān)鍵字,可以用它來(lái)編寫(xiě)“動(dòng)態(tài)”的代碼。
例如,以下代碼創(chuàng)建了一個(gè)ExpandoObject對(duì)象(注意必須定義為dynamic):
這一對(duì)象的奇特之處在于,我們可以隨時(shí)給它增加新成員:
這些動(dòng)態(tài)添加的成員與普通的類成員用法一樣:
ExpandoObject對(duì)象實(shí)現(xiàn)了IDictionary<string, object>接口,可看成是一個(gè)字典對(duì)象,所有動(dòng)態(tài)添加的成員都是這個(gè)字典對(duì)象中的元素,這意味我們不僅可以添加新成員,還可以隨時(shí)移除不再需要的成員:
方法移除之后,再嘗試訪問(wèn)此方法將引發(fā)RuntimeBinderException異常。
(2)使用dynamic關(guān)鍵字簡(jiǎn)化與COM組件交互的代碼
要在.NET這個(gè)“托管世界”里調(diào)用“非托管世界”中的COM組件,我們必須通過(guò) “互操作程序集(Interop Assembly)”作為橋梁,“互操作程序集”定義了CLR類型與COM類型之間的對(duì)應(yīng)關(guān)系。
只要給.NET項(xiàng)目添加對(duì)“互操作程序集”的引用,就可以在.NET應(yīng)用程序中創(chuàng)建這一程序集所包容的各種類型的實(shí)例(即COM包裝器對(duì)象),對(duì)這些對(duì)象的方法調(diào)用(或?qū)ζ鋵傩缘拇嫒。?huì)被轉(zhuǎn)發(fā)給COM組件。
以調(diào)用Word為例,在C# 4.0之前您可能經(jīng)常需要編寫(xiě)這樣的代碼:
上述對(duì)Open()方法的調(diào)用語(yǔ)句只能用“恐怖”一詞來(lái)形容,其原因是Word組件中的Open()方法定義了太多的參數(shù)。
C#4使用dynamic關(guān)鍵字,配合從Visual Basic中學(xué)來(lái)的“命名參數(shù)與可選參數(shù)”這兩個(gè)新語(yǔ)法特性,可以寫(xiě)出更簡(jiǎn)潔的代碼:
上述代碼中省去了用不著的參數(shù),并且可以去掉參數(shù)前的ref關(guān)鍵字。
當(dāng)上述代碼運(yùn)行時(shí),DLR會(huì)使用反射技術(shù)將dynamic表達(dá)式“綁定(bind)”到COM互操作程序集中所包容的Word.Application代理對(duì)象。
(3)C# 4動(dòng)態(tài)編程技術(shù)內(nèi)幕
C#4中所定義的dynamic變量可以引用以下類型的對(duì)象:
l 傳統(tǒng)的“靜態(tài)”的CLR對(duì)象。
l COM包裝器對(duì)象。前面已經(jīng)介紹了這方面的內(nèi)容。
l 實(shí)現(xiàn)了IDynamicMetaObjectProvider接口的“動(dòng)態(tài)對(duì)象”,ExpandoObject就是這種類型對(duì)象的實(shí)例。
l 基于DLR實(shí)現(xiàn)的動(dòng)態(tài)語(yǔ)言(比如IronRuby和IronPython)所創(chuàng)建的對(duì)象。
從C#程序員角度來(lái)看,所有這四種對(duì)象都是一樣的,都可用一個(gè)dynamic變量引用之,而DLR在程序運(yùn)行時(shí)動(dòng)態(tài)地將方法調(diào)用和字段存取請(qǐng)求“綁定”到真正的對(duì)象上。
dynamic的功能是由DLR所支撐的,是C#編譯器與DLR分工合作的成果。
請(qǐng)看以下示例代碼:
C#編譯器在處理上述代碼時(shí),它并不去檢查變量d是否可以支持自增操作,而是為其創(chuàng)建了一個(gè)CallSite<T>對(duì)象(<>p__Site1):
中文MSDN將CallSite<T>譯為“動(dòng)態(tài)(調(diào)用)站點(diǎn)”,它是DLR中的核心組件之一。
動(dòng)態(tài)站點(diǎn)對(duì)象通過(guò)CallSite<T>.Create()方法創(chuàng)建, C#編譯器會(huì)為其指定一個(gè)派生自CallSiteBinder的對(duì)象(稱為“動(dòng)態(tài)站點(diǎn)綁定對(duì)象”)作為其參數(shù)。
動(dòng)態(tài)站點(diǎn)綁定對(duì)象是與具體語(yǔ)言相關(guān)的,比如IronPython和C#都有各自的動(dòng)態(tài)站點(diǎn)綁定對(duì)象。
動(dòng)態(tài)站點(diǎn)綁定對(duì)象的主要工作是將代碼中的動(dòng)態(tài)表達(dá)式(本例中為d++)轉(zhuǎn)換為一棵“抽象語(yǔ)法樹(shù)(AST:Abstract Syntax Tree)”,這棵語(yǔ)法樹(shù)被稱為“DLR Tree”,是在.NET 3.5所引入的LINQ表達(dá)式樹(shù)的基礎(chǔ)上擴(kuò)充而來(lái)的,因此,有時(shí)又稱其為“表達(dá)式樹(shù)(Expression Tree)”
DLR在內(nèi)部調(diào)用此表達(dá)式樹(shù)的Compile()方法生成IL指令,得到一個(gè)可以被CLR所執(zhí)行的委托(在本例中其類型就是Func<CallSite, object, object>)。
動(dòng)態(tài)調(diào)用站點(diǎn)對(duì)象(本例中為<>p__Site1)有一個(gè)Target屬性,它負(fù)責(zé)引用這一生成好的委托。
委托生成之后,動(dòng)態(tài)表達(dá)式的執(zhí)行就體現(xiàn)為委托的執(zhí)行,其實(shí)參由C#編譯器直接“寫(xiě)死”在IL代碼中。
簡(jiǎn)化的代碼示意如下(通過(guò)Reflector得到,為便于閱讀,修改了變量名):
上述類型推斷、方法綁定及IL代碼生成的工作都是在程序運(yùn)行時(shí)完成的。
(4)動(dòng)態(tài)代碼很慢嗎?
動(dòng)態(tài)編程語(yǔ)言易學(xué)易用,代碼緊湊,開(kāi)發(fā)靈活,但性能則一直是它的“軟肋”。為了提升性能,DLR設(shè)計(jì)了一個(gè)三級(jí)緩存策略。
動(dòng)態(tài)站點(diǎn)綁定對(duì)象會(huì)為動(dòng)態(tài)調(diào)用表達(dá)式轉(zhuǎn)換而成的語(yǔ)法樹(shù)加上相應(yīng)的測(cè)試條件(稱為“test”),構(gòu)成一個(gè)“規(guī)則(Rule)”,這個(gè)規(guī)則可以用于判斷某個(gè)語(yǔ)法樹(shù)是否可用于特定的動(dòng)態(tài)調(diào)用表達(dá)式。
舉個(gè)例子,請(qǐng)看以下這個(gè)動(dòng)態(tài)表達(dá)式:
d1 + d2
如果在程序運(yùn)行時(shí)d1和d2都是int類型的整數(shù),則DLR生成的規(guī)則為:
DLR通過(guò)檢查規(guī)則中的“測(cè)試條件”,就可以知道某個(gè)動(dòng)態(tài)表達(dá)式是否可以使用此規(guī)則所包容的語(yǔ)法樹(shù)。
“規(guī)則”是DLR緩存的主要對(duì)象。
前面介紹過(guò)的動(dòng)態(tài)站點(diǎn)對(duì)象Target屬性所引用的委托是第一級(jí)緩存,它實(shí)現(xiàn)的處理邏輯是這樣的:
如果3級(jí)緩存中都沒(méi)有命中的規(guī)則,則此動(dòng)態(tài)站點(diǎn)所關(guān)聯(lián)的調(diào)用站點(diǎn)綁定對(duì)象會(huì)嘗試創(chuàng)建一個(gè)新的規(guī)則。如果創(chuàng)建新規(guī)則失敗,則由當(dāng)前編程語(yǔ)言(比如C#)所提供的默認(rèn)調(diào)用站點(diǎn)綁定對(duì)象決定如何處理,通常的作法是拋出一個(gè)異常。
當(dāng)前版本的DLR第2級(jí)緩存了10條規(guī)則,第3級(jí)則緩存了100條規(guī)則。
由于DLR自身設(shè)計(jì)了一個(gè)“規(guī)則”緩存系統(tǒng),又充分利用了CLR所提供的JIT緩存(因?yàn)樗袆?dòng)態(tài)調(diào)用代碼最終都會(huì)轉(zhuǎn)換為CLR可以執(zhí)行的IL指令,而CLR可以緩存這些代碼),使得動(dòng)態(tài)代碼僅僅在第一次執(zhí)行時(shí)性能較差,后續(xù)的連續(xù)調(diào)用其性能可以逼近靜態(tài)代碼。
3 C# 4與動(dòng)態(tài)語(yǔ)言的集成
由于幾乎所有的編程語(yǔ)言都可以使用抽象語(yǔ)法樹(shù)來(lái)表達(dá),因此,在理論上DLR支持無(wú)限多種編程語(yǔ)言間的互操作,在當(dāng)前版本中,可以實(shí)現(xiàn)C#/Visual Basic與IronPython和IronRuby的互操作,相信很快會(huì)出現(xiàn)其他動(dòng)態(tài)編程語(yǔ)言的DLR實(shí)現(xiàn)。
一個(gè)有趣的地方是當(dāng)前基于DLR實(shí)現(xiàn)的動(dòng)態(tài)編程語(yǔ)言都以“Iron”開(kāi)頭,比如IronRuby和IronPython。IronPython的設(shè)計(jì)者、DLR的架構(gòu)設(shè)計(jì)師Jim Hugunin曾經(jīng)在微軟PDC 2008大會(huì)上解釋說(shuō)主要是為了避免起一個(gè)“Python.NET”或“Python for .NET”之類“微軟味十足”的名字,才有了“IronPython”。他強(qiáng)調(diào):“Iron”系列動(dòng)態(tài)語(yǔ)言將嚴(yán)格遵循動(dòng)態(tài)語(yǔ)言自身的標(biāo)準(zhǔn)和規(guī)范,尊重這些動(dòng)態(tài)語(yǔ)言已有的歷史和積累,不會(huì)引入一些僅限于.NET平臺(tái)的新語(yǔ)言特性,并且這些語(yǔ)言的.NET實(shí)現(xiàn)保持開(kāi)源。與此同時(shí),Jim Hugunin指出 “Iron”系列語(yǔ)言能很好地與.NET現(xiàn)有類庫(kù)、編程語(yǔ)言和工具集成,并且能“嵌入”到.NET宿主程序中。
(1)動(dòng)態(tài)對(duì)象通訊協(xié)議
由于各種動(dòng)態(tài)編程語(yǔ)言之間的特性相差極大,實(shí)現(xiàn)各語(yǔ)言間的互操作是個(gè)難題。為此DLR采取了一個(gè)聰明的策略,它不去嘗試設(shè)計(jì)一個(gè)“通用的類型系統(tǒng)”(CLR就是這么干的),而是設(shè)計(jì)了一個(gè)“通用的對(duì)象通訊協(xié)議”,規(guī)定所有需要互操作的動(dòng)態(tài)對(duì)象必須實(shí)現(xiàn)IDynamicMetaObjectProvider接口,此接口定義了一個(gè)GetMetaObject()方法,接收一個(gè)語(yǔ)法樹(shù)對(duì)象作為參數(shù),向外界返回一個(gè)“動(dòng)態(tài)元數(shù)據(jù)(DynamicMetaObject)”對(duì)象:
DynamicMetaObject對(duì)象向外界提供了兩個(gè)重要屬性:Restrictions引用一組測(cè)試條件,Expression屬性則引用一個(gè)語(yǔ)法樹(shù)。這兩個(gè)屬性組合起來(lái)就是可供動(dòng)態(tài)站點(diǎn)對(duì)象緩存的“規(guī)則(Rule)”。
DLR中的“動(dòng)態(tài)站點(diǎn)綁定對(duì)象(CallSiteBinder)”獲取了DynamicMetaObject對(duì)象之后,它調(diào)用此對(duì)象所提供的各個(gè)方法創(chuàng)建“規(guī)則”,讓“動(dòng)態(tài)站點(diǎn)對(duì)象(CallSite<T>)”的Target屬性引用它,完成動(dòng)態(tài)綁定的工作。
(2)動(dòng)態(tài)語(yǔ)言集成環(huán)境
為了方便地實(shí)現(xiàn)靜態(tài)編程語(yǔ)言與各種動(dòng)態(tài)編程語(yǔ)言間的相互集成,DLR提供了一整套稱為“通用寄宿(Common Hosting)”的組件,其中包容ScriptRuntime、ScriptScope等類型。
下面我們以IronPython為例,介紹如何在C# 4開(kāi)發(fā)的程序中集成動(dòng)態(tài)編程語(yǔ)言代碼。
首先需要?jiǎng)?chuàng)建一個(gè)ScriptRuntime對(duì)象,它是一個(gè)最頂層的對(duì)象,用于在一個(gè).NET應(yīng)用程序域中“嵌入”一個(gè)特定動(dòng)態(tài)語(yǔ)言的運(yùn)行環(huán)境:
接著需要?jiǎng)?chuàng)建一個(gè)ScriptEngine對(duì)象,它是動(dòng)態(tài)語(yǔ)言代碼的執(zhí)行引擎:
ScriptScope對(duì)象類似于C#中的命名空間,其中可以通過(guò)定義一些變量向動(dòng)態(tài)代碼傳入數(shù)據(jù),比如下述代碼將一個(gè)C# 創(chuàng)建的ExpandoObject對(duì)象傳給Python代碼:
上述示例代碼是直接執(zhí)行Python代碼。在實(shí)際開(kāi)發(fā)中,更常見(jiàn)的是直接執(zhí)行Python文件中的代碼,假設(shè)有一個(gè)Calculator.py文件,其中定義了一個(gè)Add函數(shù):
def Add(a,b):
return a+b
則以下C#代碼可以直接執(zhí)行之:
上述示例說(shuō)明在DLR的支持之下,可以讓靜態(tài)編程語(yǔ)言使用動(dòng)態(tài)語(yǔ)言所開(kāi)發(fā)的庫(kù),反過(guò)來(lái),基于DLR實(shí)現(xiàn)的動(dòng)態(tài)編程語(yǔ)言也能使用為靜態(tài)語(yǔ)言所設(shè)計(jì)的庫(kù),比如標(biāo)準(zhǔn)的.NET基類庫(kù)。
這意味著兩點(diǎn):
(1)我們現(xiàn)在可以將“靜態(tài)”和“動(dòng)態(tài)”編程語(yǔ)言組合起來(lái),開(kāi)發(fā)出一些具有高度交互性的應(yīng)用程序,使用靜態(tài)編程語(yǔ)言搭建系統(tǒng)框架,使用動(dòng)態(tài)編程語(yǔ)言實(shí)現(xiàn)交互性,這是一個(gè)很值得注意的應(yīng)用領(lǐng)域。
(2)將來(lái)會(huì)出現(xiàn)一些“靜態(tài)”“動(dòng)態(tài)”編程語(yǔ)言同時(shí)適用的庫(kù),向?qū)?現(xiàn)“無(wú)所不在的復(fù)用”目標(biāo)又前進(jìn)了一步。
Visual Studio 2010為新的.NET編程語(yǔ)言F#提供了專門的項(xiàng)目模板,但沒(méi)有為IronPython和IronRuby之類動(dòng)態(tài)語(yǔ)言的開(kāi)發(fā)提供支持,相信隨著動(dòng)態(tài)語(yǔ)言在.NET平臺(tái)之上的應(yīng)用日趨廣泛,后繼版本的Visual Studio會(huì)直接支持動(dòng)態(tài)語(yǔ)言的開(kāi)發(fā)。
從C# 1.0~4.0所走過(guò)的路,可以很清晰地看到它的發(fā)展軌跡,得到這樣的一個(gè)結(jié)論:
未來(lái)的編程語(yǔ)言應(yīng)該是多范式的,具有高度的可組合性,在一個(gè)項(xiàng)目或產(chǎn)品中組合多個(gè)編程語(yǔ)言、使用多種編程范式會(huì)變得越來(lái)越普遍。
我們可以推斷C#的后繼版本將會(huì)在此條道路上越走越遠(yuǎn)……
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請(qǐng)務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請(qǐng)郵件反饋至chenjj@ke049m.cn
文章轉(zhuǎn)載自:博客園