淺談C#泛型的用處與特點(diǎn)
// Declare the generic class
public class GenericList<T>
{
void Add(T input) { }
}
class TestGenericList
{
private class ExampleClass { }
static void Main()
{
// Declare a list of type int
GenericList<int> list1 = new GenericList<int>();
// Declare a list of type string
GenericList<string> list2 = new GenericList<string>();
// Declare a list of type ExampleClass
GenericList<ExampleClass> list3 = new GenericList<ExampleClass>();
}
}
使用泛型類(lèi)型可以最大限度地重用代碼、保護(hù)類(lèi)型的安全以及提高性能。
泛型最常見(jiàn)的用途是創(chuàng)建集合類(lèi)。
.NET Framework 類(lèi)庫(kù)在 System.Collections.Generic 命名空間中包含幾個(gè)新的泛型集合類(lèi)。應(yīng)盡可能地使用這些類(lèi)來(lái)代替普通的類(lèi),如 System.Collections 命名空間中的 ArrayList。
您可以創(chuàng)建自己的泛型接口、泛型類(lèi)、泛型方法、泛型事件和泛型委托。
可以對(duì)泛型類(lèi)進(jìn)行約束以訪問(wèn)特定數(shù)據(jù)類(lèi)型的方法。
關(guān)于泛型數(shù)據(jù)類(lèi)型中使用的類(lèi)型的信息可在運(yùn)行時(shí)通過(guò)反射獲取。
為什么要使用C#泛型?
為了了解這個(gè)問(wèn)題,我們先看下面的代碼,代碼省略了一些內(nèi)容,但功能是實(shí)現(xiàn)一個(gè)棧,這個(gè)棧只能處理int數(shù)據(jù)類(lèi)型:
publicclassStack
{
privateint[]m_item;
publicintPop(){...}
publicvoidPush(intitem){...}
publicStack(inti)
{
this.m_item=newint[i];
}
}
上面代碼運(yùn)行的很好,但是,當(dāng)我們需要一個(gè)棧來(lái)保存string類(lèi)型時(shí),該怎么辦呢?很多人都會(huì)想到把上面的代碼復(fù)制一份,把int改成string不就行了。當(dāng)然,這樣做本身是沒(méi)有任何問(wèn)題的,但一個(gè)優(yōu)秀的程序是不會(huì)這樣做的,因?yàn)樗氲饺粢院笤傩枰猯ong、Node類(lèi)型的棧該怎樣做呢?還要再?gòu)?fù)制嗎??jī)?yōu)秀的程序員會(huì)想到用一個(gè)通用的數(shù)據(jù)類(lèi)型object來(lái)實(shí)現(xiàn)這個(gè)棧:
publicclassStack
{
privateobject[]m_item;
publicobjectPop(){...}
publicvoidPush(objectitem){...}
publicStack(inti)
{
this.m_item=new[i];
}
}
這個(gè)棧寫(xiě)的不錯(cuò),他非常靈活,可以接收任何數(shù)據(jù)類(lèi)型,可以說(shuō)是一勞永逸。但全面地講,也不是沒(méi)有缺陷的,主要表現(xiàn)在:
當(dāng)Stack處理值類(lèi)型時(shí),會(huì)出現(xiàn)裝箱、折箱操作,這將在托管堆上分配和回收大量的變量,若數(shù)據(jù)量大,則性能損失非常嚴(yán)重。
在處理引用類(lèi)型時(shí),雖然沒(méi)有裝箱和折箱操作,但將用到數(shù)據(jù)類(lèi)型的強(qiáng)制轉(zhuǎn)換操作,增加處理器的負(fù)擔(dān)。
在數(shù)據(jù)類(lèi)型的強(qiáng)制轉(zhuǎn)換上還有更嚴(yán)重的問(wèn)題(假設(shè)stack是Stack的一個(gè)實(shí)例):
Node1x=newNode1();
stack.Push(x);
Node2y=(Node2)stack.Pop();
上面的代碼在編譯時(shí)是完全沒(méi)問(wèn)題的,但由于Push了一個(gè)Node1類(lèi)型的數(shù)據(jù),但在Pop時(shí)卻要求轉(zhuǎn)換為Node2類(lèi)型,這將出現(xiàn)程序運(yùn)行時(shí)的類(lèi)型轉(zhuǎn)換異常,但卻逃離了編譯器的檢查。
針對(duì)object類(lèi)型棧的問(wèn)題,我們引入泛型,他可以?xún)?yōu)雅地解決這些問(wèn)題。泛型用用一個(gè)通過(guò)的數(shù)據(jù)類(lèi)型T來(lái)代替object,在類(lèi)實(shí)例化時(shí)指定T的類(lèi)型,運(yùn)行時(shí)(Runtime)自動(dòng)編譯為本地代碼,運(yùn)行效率和代碼質(zhì)量都有很大提高,并且保證數(shù)據(jù)類(lèi)型安全。
使用C#泛型
下面是用泛型來(lái)重寫(xiě)上面的棧,用一個(gè)通用的數(shù)據(jù)類(lèi)型T來(lái)作為一個(gè)占位符,等待在實(shí)例化時(shí)用一個(gè)實(shí)際的類(lèi)型來(lái)代替。讓我們來(lái)看看泛型的威力:
publicclassStack
{
privateT[]m_item;
publicTPop(){...}
publicvoidPush(Titem){...}
publicStack(inti)
{
this.m_item=newT[i];
}
}
類(lèi)的寫(xiě)法不變,只是引入了通用數(shù)據(jù)類(lèi)型T就可以適用于任何數(shù)據(jù)類(lèi)型,并且類(lèi)型安全的。這個(gè)類(lèi)的調(diào)用方法:
//實(shí)例化只能保存int類(lèi)型的類(lèi)
Stacka=newStack(100);
a.Push(10);
a.Push("8888");//這一行編譯不通過(guò),因?yàn)轭?lèi)a只接收int類(lèi)型的數(shù)據(jù)
intx=a.Pop();
//實(shí)例化只能保存string類(lèi)型的類(lèi)
Stackb=newStack(100);
b.Push(10);//這一行編譯不通過(guò),因?yàn)轭?lèi)b只接收string類(lèi)型的數(shù)據(jù)
b.Push("8888");
stringy=b.Pop();
這個(gè)類(lèi)和object實(shí)現(xiàn)的類(lèi)有截然不同的區(qū)別:
1.他是類(lèi)型安全的。實(shí)例化了int類(lèi)型的棧,就不能處理string類(lèi)型的數(shù)據(jù),其他數(shù)據(jù)類(lèi)型也一樣。
2.無(wú)需裝箱和折箱。這個(gè)類(lèi)在實(shí)例化時(shí),按照所傳入的數(shù)據(jù)類(lèi)型生成本地代碼,本地代碼數(shù)據(jù)類(lèi)型已確定,所以無(wú)需裝箱和折箱。
3.無(wú)需類(lèi)型轉(zhuǎn)換。
理論知識(shí):
所謂泛型:即通過(guò)參數(shù)化類(lèi)型來(lái)實(shí)現(xiàn)在同一份代碼上操作多種數(shù)據(jù)類(lèi)型。泛型編程是一種編程范式,它利用“參數(shù)化類(lèi)型”將類(lèi)型抽象化,從而實(shí)現(xiàn)更為靈活的復(fù)用。
C#泛型賦予了代碼更強(qiáng)的類(lèi)型安全,更好的復(fù)用,更高的效率,更清晰的約束。
C#泛型能力由CLR在運(yùn)行時(shí)支持,區(qū)別于C++的編譯時(shí)模板機(jī)制,和java的編譯時(shí)的“搽拭法”。這使得泛型能力可以在各個(gè)支持CLR的語(yǔ)言之間進(jìn)行無(wú)縫的互操作。
C#泛型代碼在被編譯為IL和元數(shù)據(jù)時(shí),采用特殊的占位符來(lái)表示泛型類(lèi)型,并用專(zhuān)有的IL指令支持泛型操作。而真正的泛型實(shí)例化工作以“on-demand”的方式,發(fā)生在JIT編譯時(shí)。
C#泛型編譯機(jī)制如下:
第一輪編譯時(shí),編譯器只為Stack類(lèi)型產(chǎn)生“泛型版”的IL代碼和元數(shù)據(jù),并不進(jìn)行泛型類(lèi)型的實(shí)例化,T在中間只充當(dāng)占位符。
JIT編譯時(shí),當(dāng)JIT編譯器第一次遇到Stack時(shí),將用int類(lèi)型替換“泛型版”IL代碼與元數(shù)據(jù)中的T -- 進(jìn)行泛型類(lèi)型的實(shí)例化。
CLR為所有類(lèi)型參數(shù)為“引用類(lèi)型”的泛型類(lèi)型產(chǎn)生同一份代碼,但如果類(lèi)型參數(shù)為“值類(lèi)型”,對(duì)每一個(gè)不同的“值類(lèi)型”,CLR將為其產(chǎn)生一份獨(dú)立的代碼。
C#泛型的幾個(gè)特點(diǎn)
如果實(shí)例化泛型類(lèi)型的參數(shù)相同,那么JIT編譯器會(huì)重復(fù)使用該類(lèi)型,因此C#的動(dòng)態(tài)泛型能力避免了C++靜態(tài)模板可能導(dǎo)致的代碼膨脹的問(wèn)題。
C#泛型類(lèi)型攜帶有豐富的元數(shù)據(jù),因此C#的泛型類(lèi)型可以應(yīng)用于強(qiáng)大的反射技術(shù)。
C#的泛型采用“基類(lèi)、接口、構(gòu)造器、值類(lèi)型/引用類(lèi)型”的約束方式來(lái)實(shí)現(xiàn)對(duì)類(lèi)型參數(shù)的“顯示約束”,提高了類(lèi)型安全的同時(shí),也喪失了C++模板基于“簽名”的隱式約束所具有的高靈活性。
C#泛型類(lèi)在編譯時(shí),先生成中間代碼IL,通用類(lèi)型T只是一個(gè)占位符。在實(shí)例化類(lèi)時(shí),根據(jù)用戶(hù)指定的數(shù)據(jù)類(lèi)型代替T并由即時(shí)編譯器(JIT)生成本地代碼,這個(gè)本地代碼中已經(jīng)使用了實(shí)際的數(shù)據(jù)類(lèi)型,等同于用實(shí)際類(lèi)型寫(xiě)的類(lèi),所以不同的封閉類(lèi)的本地代碼是不一樣的。按照這個(gè)原理,我們可以這樣認(rèn)為:泛型類(lèi)的不同的封閉類(lèi)是分別不同的數(shù)據(jù)類(lèi)型。
這樣泛型不僅更加靈活,也同時(shí)將代碼的簡(jiǎn)便和提高到一個(gè)層次!不用再為具體不同的重載方法寫(xiě)具體的代碼了!
C# 泛型是開(kāi)發(fā)工具庫(kù)中的一個(gè)無(wú)價(jià)之寶。它們可以提高性能、類(lèi)型安全和質(zhì)量,減少重復(fù)性的編程任務(wù),簡(jiǎn)化總體編程模型,而這一切都是通過(guò)優(yōu)雅的、可讀性強(qiáng)的語(yǔ)法完成的。盡管 C# 泛型的根基是 C++ 模板,但 C# 通過(guò)提供編譯時(shí)安全和支持將泛型提高到了一個(gè)新水平。C# 利用了兩階段編譯、元數(shù)據(jù)以及諸如約束和一般方法之類(lèi)的創(chuàng)新性的概念。毫無(wú)疑問(wèn),C# 的將來(lái)版本將繼續(xù)發(fā)展泛型,以便添加新的功能,并且將泛型擴(kuò)展到諸如數(shù)據(jù)訪問(wèn)或本地化之類(lèi)的其他 .NET Framework 領(lǐng)域。
當(dāng)然,C#的泛型還很多應(yīng)用,現(xiàn)在我還只是了解了它的機(jī)制和原理,在接下來(lái)的學(xué)習(xí)中我會(huì)系統(tǒng)得學(xué)習(xí)泛型所支持的抽象泛型,接口泛型,結(jié)構(gòu)和委托等!
相關(guān)文章
c# 調(diào)用Surfer軟件,添加引用的具體操作方法
本篇文章主要是對(duì)c#中調(diào)用Surfer軟件,添加引用的具體操作方法進(jìn)行了介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2014-01-01C#實(shí)現(xiàn)基于XML配置MenuStrip菜單的方法
這篇文章主要介紹了C#實(shí)現(xiàn)基于XML配置MenuStrip菜單的方法,涉及C#使用XML配置MenuStrip菜單的原理與實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-08-08C#針對(duì)xml基本操作及保存配置文件應(yīng)用實(shí)例
這篇文章主要介紹了C#針對(duì)xml基本操作及保存配置文件應(yīng)用實(shí)例,包括了針對(duì)XML文件的定義、初始化、創(chuàng)建、以及增刪改查等基礎(chǔ)操作,并配有詳細(xì)的實(shí)例加以說(shuō)明,需要的朋友可以參考下2014-10-10LINQ基礎(chǔ)之Intersect、Except和Distinct子句
這篇文章介紹了LINQ使用Intersect、Except和Distinct子句的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-04-04WPF+ASP.NET SignalR實(shí)現(xiàn)動(dòng)態(tài)折線圖的繪制
這篇文章將以一個(gè)簡(jiǎn)單的動(dòng)態(tài)折線圖示例,簡(jiǎn)述如何通過(guò)ASP.NET SignalR實(shí)現(xiàn)后臺(tái)通知功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2023-01-01C#數(shù)據(jù)表格(DataGridView)控件的應(yīng)用案例
這篇文章主要介紹了C#數(shù)據(jù)表格(DataGridView)控件的應(yīng)用案例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03