轉(zhuǎn)帖|其它|編輯:郝浩|2010-06-10 09:38:44.000|閱讀 757 次
概述:靜態(tài)類型檢查的引入是編程語言史上一個重要的里程碑。在二十世紀(jì)七十年代,Pascal 和 C 等語言開始執(zhí)行靜態(tài)類型和強(qiáng)類型檢查。有了靜態(tài)類型檢查,對于任何無法傳遞相應(yīng)類型方法參數(shù)的調(diào)用,編譯器都將產(chǎn)生錯誤。同樣,如果您試圖調(diào)用類型實(shí)例中不存在的方法,編譯器也應(yīng)該會產(chǎn)生錯誤。
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
靜態(tài)類型檢查的引入是編程語言史上一個重要的里程碑。在二十世紀(jì)七十年代,Pascal 和 C 等語言開始執(zhí)行靜態(tài)類型和強(qiáng)類型檢查。有了靜態(tài)類型檢查,對于任何無法傳遞相應(yīng)類型方法參數(shù)的調(diào)用,編譯器都將產(chǎn)生錯誤。同樣,如果您試圖調(diào)用類型實(shí)例中不存在的方法,編譯器也應(yīng)該會產(chǎn)生錯誤。
推進(jìn)動態(tài)類型檢查這一相對應(yīng)方法的其他語言在過去幾年有所發(fā)展。動態(tài)類型檢查否定了以下這一觀點(diǎn):即變量類型是在編譯時靜態(tài)確定的,當(dāng)變量在作用域內(nèi)時應(yīng)該永遠(yuǎn)不變。不過請注意,動態(tài)類型檢查并非認(rèn)為各個類型都是相同的,可以對類型進(jìn)行自由組合。例如,即使使用動態(tài)類型檢查,您仍不能為整數(shù)添加布爾值。動態(tài)類型檢查的不同之處在于,檢查是發(fā)生在程序執(zhí)行時,而不是編譯時。
靜態(tài)類型化與動態(tài)類型化
Visual Studio 2010 和 C# 4.0 提供了新的動態(tài)關(guān)鍵字,能夠在傳統(tǒng)上采用靜態(tài)類型化的語言中實(shí)現(xiàn)動態(tài)類型化。不過,在深入了解 C#4.0 的動態(tài)方面之前,我們需要記錄一些基本術(shù)語。
讓我們定義某個變量,作為只能采用特定類型數(shù)值的存儲位置。下一步,讓我們說明靜態(tài)類型化語言的四個基本屬性:
每個表達(dá)式都屬于已在編譯時確定的類型。
變量只能屬于已在編譯時確定的類型。
編譯器保證在將表達(dá)式指定給變量時采用的類型限制滿足對變量的限制。
重載解析等語義分析任務(wù)發(fā)生在編譯時,其結(jié)果會輸入到程序集。
動態(tài)語言的屬性正相反。每個表達(dá)式和每個變量的類型并未在編譯時就確定。存儲限制(如果有的話)是在運(yùn)行時進(jìn)行檢查,而在編譯時會被忽略。語義分析也只發(fā)生在運(yùn)行時。
靜態(tài)類型化語言確實(shí)可以實(shí)現(xiàn)某些操作的動態(tài)化。它們提供了轉(zhuǎn)換運(yùn)算符,供您作為運(yùn)行時操作嘗試進(jìn)行類型轉(zhuǎn)換。程序代碼中已加入了轉(zhuǎn)換功能,您可以將轉(zhuǎn)換運(yùn)算符所表達(dá)的語義總結(jié)為“動態(tài)檢查此轉(zhuǎn)換在運(yùn)行時的有效性。”
不過,就動態(tài)和靜態(tài)(也可以說是強(qiáng)和弱)等屬性而言:目前與應(yīng)用到整個編程語言相比,應(yīng)用到語言的各個功能效果更好。
讓我們大致考察一下 Python 和 PHP。這兩者都是動態(tài)語言,可供您使用變量,并提供了運(yùn)行時環(huán)境,可獲知實(shí)際存儲在其中的類型。不過如果使用 PHP,您可以在同一作用域的同一變量中同時存儲整數(shù)和字符串等等。從這一意義上講,PHP(類似于 JavaScript)是一種弱類型化的動態(tài)語言。
與之相對,Python 只會給您一次機(jī)會來設(shè)置變量類型,這使得它更貼近強(qiáng)類型化。您可以對變量動態(tài)指定類型,并由運(yùn)行時環(huán)境從指定值推斷其類型。不過,在此之后,您不能在此變量中存儲任何不當(dāng)類型的值。
C# 中的動態(tài)類型
C#4.0 所擁有的功能使其兼具動態(tài)和靜態(tài)以及弱類型化和強(qiáng)類型化這兩種特點(diǎn)。C# 原本屬于靜態(tài)類型化語言,但在使用動態(tài)關(guān)鍵字的任何上下文中,它都可以成為動態(tài)類型化語言,如下所示:
dynamic number = 10; Console.WriteLine(number); |
而且因?yàn)閯討B(tài)關(guān)鍵字是上下文關(guān)鍵字,而不是保留關(guān)鍵字,如果您有現(xiàn)有變量或由方法命名的動態(tài)關(guān)鍵字,這一點(diǎn)仍然成立。
請注意,C#4.0 不強(qiáng)制您使用動態(tài)關(guān)鍵字,就像 C#3.0 不強(qiáng)制您使用 var、lambda 或?qū)ο蟪跏贾狄粯印#4.0 提供了新的動態(tài)關(guān)鍵字,專門用于簡化對一些眾所周知的情形的處理。這種語言盡管有能力以更有效的方式與動態(tài)對象互動,從本質(zhì)上講它仍然屬于靜態(tài)類型化語言。
為什么要使用動態(tài)對象?首先,您可能不知道正在處理的對象的類型。對于如何確定給定變量的靜態(tài)類型,您可能有線索,但卻拿不準(zhǔn):這種情況非常常見,例如當(dāng)您處理 COM 對象或使用反射來抓取實(shí)例時,就會發(fā)生這種情況。這種情況下即可使用動態(tài)關(guān)鍵字來簡化某些操作。采用動態(tài)方式書寫的代碼更易于讀寫,這樣便于理解與維護(hù)應(yīng)用程序。
其次,您的對象從本質(zhì)上講可能就是變化的。您處理的可能正是 IronPython 和 IronRuby 等動態(tài)編程環(huán)境所生成的對象。不過,您還可以使用此功能來處理 HTML DOM 對象(這取決于 expando 屬性)和在創(chuàng)建時專門指定了動態(tài)屬性的 Microsoft .net Framework 4 對象。
使用動態(tài)變量
一定要理解以下概念:在 C# 類型系統(tǒng)中,動態(tài)是一種類型。動態(tài)的含義非常特殊,但它絕對是一種類型,一定要將它看作類型。您可以將動態(tài)指定為自己所聲明變量的類型、集合中的項(xiàng)目類型或某方法的返回值,還可以將動態(tài)用作方法參數(shù)的類型;不過,您不可以將動態(tài)用于運(yùn)算符類型,也不可以將其用作類的基類型。
下面的代碼說明了如何在方法主體中聲明動態(tài)變量:
public void Execute() { dynamic calc = GetCalculator(); int result = calc.Sum(1, 1); } |
如果您充分了解由 GetCalculator 方法返回的對象類型,您可以聲明該類型的變量 calc,也可以作為 var 聲明變量,以供編譯器了解具體細(xì)節(jié)。不過,使用 var 或顯式靜態(tài)類型,要求您確定 GetCalculator 所返回類型的約定上存在 Sum 方法。如果該方法不存在,您就會收到編譯器錯誤。
采用動態(tài)方法,您可以推遲到執(zhí)行時再確定表達(dá)式是否正確。只要方法 Sum 存在于變量 calc 所存儲的類型中,代碼即會得到編譯,并在運(yùn)行時得到解析。
您還可以使用此關(guān)鍵字在類上定義屬性。這樣做,您就可以用公共、受保護(hù)甚至靜態(tài)等所需的任何可見性修飾符修飾此成員。
圖 1 顯示了動態(tài)關(guān)鍵字的通用性。主程序包含了根據(jù)某函數(shù)調(diào)用的返回值進(jìn)行實(shí)例化的動態(tài)變量。因?yàn)榇撕瘮?shù)會接收并返回動態(tài)對象,所以即便沒有實(shí)現(xiàn)實(shí)例化,也無關(guān)緊要。在這個例子中,您傳遞一個數(shù)字,然后嘗試在此函數(shù)中對它進(jìn)行 double 操作,看看發(fā)生什么,會很有趣。
圖 1 在函數(shù)的簽名中使用動態(tài)屬性
class Program { static void Main(string[] args) { // The dynamic variable gets the return // value of a function call and outputs it. dynamic x = DoubleIt(2); Console.WriteLine(x); // Stop and wait Console.WriteLine(“Press any key”); Console.ReadLine(); } // The function receives and returns a dynamic object private static dynamic DoubleIt(dynamic p) { // Attempt to "double" the argument whatever // that happens to produce return p + p; } } |
如果您輸入數(shù)值 2,然后運(yùn)行此代碼,您將得到數(shù)值 4;而如果您作為字符串輸入 2,您會得到 22。此函數(shù)會根據(jù)操作數(shù)的運(yùn)行時類型對 + 運(yùn)算符進(jìn)行動態(tài)解析。如果您將類型更改為 System.Object,會收到編譯錯誤,原因即在于 + 運(yùn)算符未在 System.Object 上進(jìn)行定義。動態(tài)關(guān)鍵字能使不可能變成可能。
動態(tài)與 System.Object
直到 .net Framework 4,還是只能求助于通用基類,才能使方法可根據(jù)不同的情況返回不同的類型。您可能已經(jīng)通過求助于 System.Object 解決了此問題。返回 System.Object 的函數(shù)會向調(diào)用者提供可以轉(zhuǎn)換幾乎任何內(nèi)容的實(shí)例。這種情況下,為什么還說使用動態(tài)的效果要好于使用 System.Object 呢?
在 C# 4 中,被聲明為動態(tài)的變量后面的實(shí)際類型是在運(yùn)行時加以解析,編譯器會認(rèn)定:聲明為動態(tài)的變量中的對象完全支持任何操作。也就是說,您編寫的代碼完全可以調(diào)用您認(rèn)為會在運(yùn)行時出現(xiàn)的對象上的方法,如下所示:
dynamic p = GetSomeReturnValue(); p.DoSomething(); |
在 C# 4.0 中,編譯器不會對該代碼報錯。而使用 System.Object 的類似代碼將無法進(jìn)行編譯,為了解決這一問題,需要您自己采取一定措施,如進(jìn)行反射或冒一定風(fēng)險進(jìn)行轉(zhuǎn)換。
var 與動態(tài)
var 與動態(tài)關(guān)鍵字只是表面上相似。var 認(rèn)為,此變量的類型必須被設(shè)置為初始值設(shè)定項(xiàng)的編譯時類型。
但是,動態(tài)意味著此變量的類型可以是 C#4.0 中可用的任何動態(tài)類型。動態(tài)與 var 的最終含義基本上是相反的。var 講的是加強(qiáng)與改進(jìn)靜態(tài)類型化。其目標(biāo)是確保編譯器可根據(jù)初始值設(shè)定項(xiàng)返回的確切類型推斷變量類型。
動態(tài)關(guān)鍵字的目標(biāo)是完全避免靜態(tài)類型化。用于變量聲明中時,動態(tài)會指示編譯器完全停止求解變量類型。該類型需要采納它在運(yùn)行時的類型。而使用 var 時,您的代碼會采用靜態(tài)類型化方式,其結(jié)果與選擇在變量聲明中使用顯式類型的經(jīng)典方式一致。
兩類關(guān)鍵字之間的另一區(qū)別是:var 只能出現(xiàn)在局部變量聲明中。您不能使用 var 來定義類屬性,也不能用它來指定返回值或函數(shù)的參數(shù)。
作為一名開發(fā)人員,如果預(yù)計(jì)變量包含不確定類型的對象,您就應(yīng)該使用動態(tài)關(guān)鍵字,如對象從 COM 或 DOM API 返回;從動態(tài)語言(如 IronRuby)獲得;從反射獲得;從使用新擴(kuò)展功能在 C# 4.0 中動態(tài)構(gòu)建的對象中獲得。
不過,動態(tài)類型不是繞過類型檢查,只是將其全部移至運(yùn)行時。如果在運(yùn)行時發(fā)現(xiàn)不兼容的類型,則引出異常。
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請郵件反饋至chenjj@fc6vip.cn
文章轉(zhuǎn)載自:網(wǎng)絡(luò)轉(zhuǎn)載