C#中所謂泛型:即通過參數(shù)化類型來實(shí)現(xiàn)在同一份代碼上操作多種數(shù)據(jù)類型。泛型編程是一種編程范式,它利用“參數(shù)化類型”將類型抽象化,從而實(shí)現(xiàn)更為靈活的復(fù)用。
C#泛型賦予了代碼更強(qiáng)的類型安全,更好的復(fù)用,更高的效率,更清晰的約束。
C#泛型機(jī)制簡(jiǎn)介
C#泛型能力由CLR在運(yùn)行時(shí)支持,區(qū)別于C++的編譯時(shí)模板機(jī)制,和java的編譯時(shí)的“搽拭法”。這使得泛型能力可以在各個(gè)支持CLR的語言之間進(jìn)行無縫的互操作。
C#泛型代碼在被編譯為IL和元數(shù)據(jù)時(shí),采用特殊的占位符來表示泛型類型,并用專有的IL指令支持泛型操作。而真正的泛型實(shí)例化工作以“on-demand”的方式,發(fā)生在JIT編譯時(shí)。
C#泛型編譯機(jī)制
第一輪編譯時(shí),編譯器只為Stack類型產(chǎn)生“泛型版”的IL代碼和元數(shù)據(jù),并不進(jìn)行泛型類型的實(shí)例化,T在中間只充當(dāng)占位符。
JIT編譯時(shí),當(dāng)JIT編譯器第一次遇到Stack時(shí),將用int類型替換“泛型版”IL代碼與元數(shù)據(jù)中的T -- 進(jìn)行泛型類型的實(shí)例化。
CLR為所有類型參數(shù)為“引用類型”的泛型類型產(chǎn)生同一份代碼,但如果類型參數(shù)為“值類型”,對(duì)每一個(gè)不同的“值類型”,CLR將為其產(chǎn)生一份獨(dú)立的代碼。
C#泛型的幾個(gè)特點(diǎn)
如果實(shí)例化泛型類型的參數(shù)相同,那么JIT編譯器會(huì)重復(fù)使用該類型,因此C#的動(dòng)態(tài)泛型能力避免了C++靜態(tài)模板可能導(dǎo)致的代碼膨脹的問題。
C#泛型類型攜帶有豐富的元數(shù)據(jù),因此C#的泛型類型可以應(yīng)用于強(qiáng)大的反射技術(shù)。
C#的泛型采用“基類、接口、構(gòu)造器、值類型/引用類型”的約束方式來實(shí)現(xiàn)對(duì)類型參數(shù)的“顯示約束”,提高了類型安全的同時(shí),也喪失了C++模板基于“簽名”的隱式約束所具有的高靈活性。
C#泛型類與結(jié)構(gòu)
class C{} //合法
class D:C{} //合法
class E:C{} //合法
class F:C{} //合法
class G:C{} //非法
C#除可單獨(dú)聲明泛型類型(包括類與結(jié)構(gòu))外,也可在基類中包含泛型類型的聲明。但基類如果是泛型類,他的類型參數(shù)要么已實(shí)例化,要么來源子類(同樣是泛型類型)聲明的類型參數(shù)。
泛型類型的成員
class C { public V f1; //聲明字段 public D f2; //作為其他泛型類型 的參數(shù) public C { this.f1 = x; } } 泛型類型的成員可以使用泛型類型聲明中的類型參數(shù)。但類型參數(shù)如果沒有任何約束,則只能在該類型上使用從System.Object繼承的共有成員。
泛型接口
interface IList { T[] GetElements(); } interface IDictionary { void Add(K key,V value); } //泛型接口的類型參數(shù)要么已實(shí)例化 //要么來源于實(shí)現(xiàn)類聲明的類型參數(shù) class List:IList,IDictionary { public T[] GetElements{} { return null; } public void Add(int index,T value){} } 泛型委托
delegate bool Predicate(T value); class X { static bool F(int i){...} static bool G(string s){...} static void Main() { Predicate p2 = G; Predicate p1 = new Predicate(F); } } 泛型委托支持返回值和參數(shù)哂納感應(yīng)用參數(shù)類型,這些參數(shù)類型同樣可以附帶合法的約束。
泛型方法的簡(jiǎn)介
C#泛型機(jī)制只支持“在方法聲明上包含類型參數(shù)” -- 即泛型方法。
C#泛型機(jī)制不支持在除方法外的其他成員(包括屬性、事件、索引器、構(gòu)造器、析構(gòu)器)的聲明上包含類型參數(shù),但這些成員本身可以包含在泛型類型中,并使用泛型類型的類型參數(shù)。
泛型方法既可以包含在泛型類型中,也可以包含在非泛型類型中。
泛型方法的聲明與調(diào)用
public class Finder { // 泛型方法的聲明 public static int Find(T[] items,T item) { for(int i=0;i { if(items[i].Equals(item) { return i; } } return -1; } } // 泛型方法的調(diào)用 int i = Finder.Find(new int[]{1,3,4,5,6,8,9},6); 泛型編程
泛型方法的重載
class MyClass { void F1(T[] a,int i); // 不可以構(gòu)成重載方法 void F1(U[] a,int i); void F2(int x); // 可以構(gòu)成重載方法 void F2(int x); void F3(T t) where T : A; // 不可以構(gòu)成重載方法 void F3(T t) where T : B; } 泛型方法的重寫
abstract class Base { public abstract T F(T t,U u) where U : T; public abstract T G(T t) where U : IComparable; } class Derived:Base { // 合法的重寫,約束被默認(rèn)繼承 public override X F(X,Y)(X x,Y y){} // 非法的重寫,指定任何約束都是多余的 public override T G(T t) where T : Comparable{} }
泛型約束簡(jiǎn)介
C#泛型要求對(duì)"所有泛型類型或泛型方法的類型參數(shù)"的任何假定,都要基于"顯式的約束",以維護(hù)C#所要求的類型安全.
"顯式約束"有where字句表達(dá),可以指定"基類約束","接口約束","構(gòu)造器約束","值類型/引用類型約束"共四中約束.
"顯示約束"并非必須,如果沒有指定"顯式約束",泛型類型參數(shù)將只能訪問System.Object類型中的公有方法.
基類約束
class A { public void F1(){} } class B { public void F2(){} } class C(S,T) where S:A // S繼承自A where T:B // T繼承自B { // 可以在類型為S的變量上調(diào)用F1 // 可以在類型為T的變量上調(diào)用F2 } 接口約束
interface IPrintable{coid Print();} interface IComparable{int CompareTo(T v);} interface IKeyProvider{T HetKey();} class Dictionary where K:IComparable where V:IPrintable,IKeyProvider { // 可以在類型為K的變量上調(diào)用CompareTo // 可以在類型為V的變量上調(diào)用Print和GetKey } 構(gòu)造器約束
class A { public A(){} } class B { public B(int i)() } class C where T:new() { // 可以在其中使用T t = new T(); } C c = new C(); // 可以,A有無參數(shù)構(gòu)造器 C c = new C(); // 錯(cuò)誤,B沒有無參數(shù)構(gòu)造器 值類型/引用類型約束
public struct A{...} public class B{...} class C where T : struct { // T在這里面是一個(gè)值類型 } C c = new C(); // 可以,A是一個(gè)值類型 C c = new C(); // 錯(cuò)誤,B是一個(gè)引用類型 總結(jié)
C#的泛型能力有CLR在運(yùn)行時(shí)支持,它既不同于c++在編譯時(shí)所支持的靜態(tài)模板,也不同于java在編譯器層面使用"檫拭法"支持的簡(jiǎn)單的類型.
C#的泛型支持包括類,結(jié)構(gòu),接口,委托共四種泛型類型,以及方法成員.
C#的泛型采用"基類,接口,構(gòu)造器,值類型/引用類型"的約束方式來實(shí)現(xiàn)對(duì)類型參數(shù)的"顯式約束",它不支持C++模板那樣的基于簽名的顯式約束.
|