韓志強
(赤峰學院計算機科學與技術系,內(nèi)蒙古赤峰024000)
對.NET平臺中泛型技術的探究
韓志強
(赤峰學院計算機科學與技術系,內(nèi)蒙古赤峰024000)
隨著項目變得日益復雜,需要用一個更好的方式來重用和定制現(xiàn)有的軟件.為了促進代碼重用,尤其是算法的重用,在.NET 2.0以后支持一個名為泛型的特性.方法因為能獲得參數(shù)而強大.同理,泛型的主旨就是類會因為能獲取“類型”參數(shù)而變的強大.泛型類使用泛型類型,可以根據(jù)需要用特定的類型替換泛型類型,從而在保證了類型安全性的基礎上,同時提高了系統(tǒng)性能.泛型不僅限于類,而且還可用于委托、接口和方法等.由此,我們可以體會到泛型是一個強大的特性議.
.NET平臺;Object類;泛型;泛型約束
若在.NET 2.0以前要創(chuàng)建一個靈活的類或方法,但該類或方法在編譯期間不知道使用什么類,就必須以object類為基礎,而object類在編譯期間沒有類型安全性,而且性能也會降低.在這里我們來分析一個沒使用泛型的類——System.Collections.Stack.它的作用是表示一個對象集合,使加入集合的最后一項作為從集合中獲取的第一項(即后進先出).我們在使用該類時,其收集的是object類型的變量.由于CLR中每個對象都是從object派生的,所以Stack無法驗證放在其中的元素是不是你希望的類型.除此之外,若從中獲取數(shù)據(jù)時,必須將返回值轉(zhuǎn)型為我們需要的類型,否則會引發(fā)異常.如果我們要使Stack類能支持多個數(shù)據(jù)類型時,假如不使用泛型,那么根本的問題在于他們必須使用一個通用的基類型,通常是object.因此上述提到的問題是不可避免,同時為使用object的類使用值類型時會使問題進一步惡化.
對此類問題最好的解決方案是——使用泛型.類似的情況還有很多,為了解決此類問題.NET平臺引入了泛型的概念.
所謂泛型,即通過參數(shù)化類型來實現(xiàn)在同一份代碼上操作多種數(shù)據(jù)類型.利用“參數(shù)化類型”將類型抽象化,從而實現(xiàn)靈活的復用.由于C#語言是專門為.NET平臺量身定制的,因此為了更直觀的了解泛型技術,我們在下面以一段C#代碼為例,來對.NET的泛型技術進行分析說明,見表1.
在上面的程序中Test是一個泛型類.T是要實例化的泛型類型“參數(shù)”.如果T被實例化為int型,那么成員變量obj就是int型的;如果T被實例化為string型,那么obj就是string類型的.根據(jù)不同的類型,上面的程序顯示出不同的值.其編譯過程說明如下:
(1)第一輪編譯時,編譯器只為Test
表1
(2)JIT編譯時,當JIT編譯器第一次遇到Test
(3)CLR為所有類型參數(shù)為“引用類型”的泛型類型產(chǎn)生同一份代碼;但是如果類型參數(shù)為“值類型”,對每一個不同的“值類型”,CLR將為其產(chǎn)生一份獨立的代碼.因為實例化一個引用類型的泛型,它在內(nèi)存中分配的大小是一樣的,但是當實例化一個值類型的時候,在內(nèi)存中分配的大小是不一樣的.
3.1 泛型類
在聲明泛型類時,需要在一對<>中指定一個類型參數(shù).例如:public class Stack
3.2 泛型接口和struct
現(xiàn)在.NET允許在C#語言的所有組成部分中使用泛型,其中包括泛型接口和struct.其聲明語法和類使用的語法完全相同.實現(xiàn)接口時,語法與非泛型類的語法是相同的.
對泛型接口的支持對于集合類來說尤其重要,使用泛型最多的地方就是集合類.假如沒有泛型,開發(fā)者就要依賴System.Collecttions命名空間中的一系列接口.和它們實現(xiàn)的類一樣,這些接口只能用于Object類型.因此這些接口要求進出這些集合類的所有訪問都執(zhí)行一次轉(zhuǎn)型.相反,使用泛型接口,就可以避免執(zhí)行轉(zhuǎn)型,因為參數(shù)化的接口能實現(xiàn)更強的編譯時綁定.
3.3 泛型委托
委托是類型安全的方法引用.通過泛型委托,委托的參數(shù)可以在以后定義.泛型委托提供了一種更靈活的方式來指定以類型安全的方式調(diào)用方法.其支持在委托返回值和參數(shù)上應用參數(shù)類型,這些參數(shù)類型同樣可以附帶合法的約束.
3.4 泛型方法
泛型方法是即使包容類不是泛型類,或者方法包含的類型參數(shù)不在泛型類的類型參數(shù)列表中,也依然使用泛型的方法.由此我們看出泛型方法既可以包含在泛型類型中,也可以包含在非泛型類型中..NET的泛型機制不支持在除方法外的其他成員(包括屬性、事件、索引器、構(gòu)造器、析構(gòu)器)的聲明上包含類型參數(shù),但這些成員本身可以包含在泛型類型中,并使用泛型類型的類型參數(shù).
泛型允許為類型參數(shù)定義約束.這些約束強迫類型遵守各種規(guī)則..NET泛型要求對“所有泛型類型或泛型方法的類型參數(shù)”的任何假定,都要基于“顯式的約束”,以維護.NET所要求的類型安全.“顯式約束”并非必須,但是如果沒有指定“顯式約束”,編譯器會認為類型參數(shù)只有從基類型Object繼承的成員,因為Object是所有類型的終極基類,因此泛型類型參數(shù)將只能訪問System.Object類型中的公有方法.
.NET中允許你為泛型類中的每個類型參數(shù)提供一個可選的約束列表,約束聲明了泛型要求的類型參數(shù)的特征,為了聲明一個約束,需要使用where關鍵字,后跟一對“參數(shù):要求”.其中,“參數(shù)”必須是泛型類型中定義的一個參數(shù),而“要求”用于限制類型從中派生的類或接口,或者限制必須存在一個默認的構(gòu)造器,或者限制使用一個值類型/引用類型約束.
Sun完全是在編譯器中為Java實現(xiàn)泛型,而不是在JVM(Java虛擬機)中.Sun這樣做的目的是為了防止因為使用了泛型而需要分發(fā)新的JVM.Java的實現(xiàn)使用了與C++中的“模板”和C#中的“泛型”相似的語法,其中包括類型參數(shù)和約束,但是由于它不區(qū)分對待值類型和引用類型,所以未修改的JVM不能為值類型支持泛型.所以Java中的泛型不具有C#那樣的執(zhí)行效率.Java編譯器需要返回數(shù)據(jù)的時候,都會插入來自指定約束的自動向下轉(zhuǎn)型(如果聲明了這樣的一個轉(zhuǎn)型的話),或者插入基本object類型(如果沒有聲明的話).除此之外,Java編譯器在編譯時生成一個具體化的類型,它隨即用于實例化任何已構(gòu)造的類型.最后由于JVM沒有提供對泛型的原生支持,所以在執(zhí)行時無法確定一個泛型類型實例的類型參數(shù),“反射”的其他應用也受到了嚴格的限制.
.NET中的的泛型能力由CLR在運行時支持,它既不同于C++在編譯時所支持的靜態(tài)模板,也不同于Java在編譯器層面使用“擦拭法”支持的簡單的泛型,因此.NET的泛型即避免了C++靜態(tài)模板可能導致的代碼膨脹的問題,又提高了執(zhí)行效率.同時.NET的泛型類型也攜帶有豐富的元數(shù)據(jù),因此.NET的泛型類型可以應用于強大的反射技術.
總之,泛型顯著改變了原有的編程風格.凡是以前使用了object類的地方,現(xiàn)在最好使用泛型來代替.換言之,凡是出現(xiàn)了object類的地方,都應該考慮泛型.泛型安全性的提升、轉(zhuǎn)型的避免以及代碼量的減少為泛型賦予了無窮的魅力.同理,凡是使用了System.Collections命名空間的地方,System.Collections.Generics都是一個更好的選擇.
TP31
A
1673-260X(2010)11-0023-02