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