一、引言
泛型的主要思想是将算法与数据结构完全分离开,使得一次定义的算法能作用于多种数据结构,从而实现高度可重用的开发。泛型,通过参数类型化来实现在同一份代码中操作多种数据类型,利用“参数化类型”将类型抽象化,从而实现更为灵活的运用。c#的泛型起源于c++的模板函数,当然在c#中进行升级。在c#中泛型实在编译时模板机制,c++是在运行时模板机制。其中泛型通过T来通知CLR在编译中利用特殊占位符表明此时在操作泛型操作,产生IL指令。
二、泛型的编译机制
在.net Framewrok 2.0 中,泛型在IL和CLR本身中具有本机支持。在编译泛型c#代码时,像其他任何类型一样,首先编译器会将其编译为IL,但是,IL只包含实际特定类型的参数或占位符,并有专用的IL指令支持泛型操作。泛型代码的元数据中包含泛型信息。真正的泛型实例化工作是以“on-demand”的方式,发生在JIT编译时。当进行JIT编译时,JIT编译器用指定的类型实参来替换泛型IL代码元数据中的T,进行泛型类型的实例化。
在第一轮编译时
第一轮编译时,编译器只为Stack(栈算法)类型产生“泛型版”的IL代码与元数据—–并不进行泛型类型的实例化,T在中间只充当占位符。JIT编译时,当JIT编译器第一次遇到Stack《T》时,将用int替换“泛型版”IL代码与元数据中的T—进行泛型类型的实例化。
CLR为所有类型参数为“引用类型”的泛型类型产生同一份代码;但如果类型参数为“值类型”,对每一个不同的“值类型”,CLR将为其产生一份独立的代码。
三、泛型约束
在泛型中应用最广的就是依据泛型约束进行拓展。C#的泛型采用“基类,接口,构造器,值类型/引用类型”的约束方式来实现对类型能数的“显式约束”,提高了类型安全的同时,也丧失了C++模板基于“签名”的隐式约束所具有的高灵活性。C#泛型要求对“所有泛型类型或泛型方法的类型参数”的任何假定,都要基于“显式的约束”,以维护C#所要求的类型安全。
在泛型应用中,要尽量显示制定约束条件,当没有显示约束时,讲默认泛型类型集成于Object。在进行泛型约束后,在对泛型类操作时可以直接操作,很是方便,有点继承的感觉。
例如:
where T : struct 类型必须是一种值类型(struct)
where T : class 类型必须是一种引用类型(class)
where T : new() 类型必须有一个无参数的构造器
where T : class_name 类型可以是class_name或者是它的一个子类
where T : interface_name 类型必须实现指定的接口
- 1、基类约束
class Stack1 { private string name; public Stack1() { } public void Func1() { } } class Statck2 { public Statck2() { } public void Func2(){} } //显示告诉Statck3中T的泛型类型继承与Statck1,V来自Statck2 class Statck3<T,V> where T:Stack1 where V:Statck2 { public Statck3(T t,V v) { //此处实例化了,所以t只能调用statck1的方法 t.Func1(); v.Func2(); } } class Statck4<T> where T :new() { T t; public Statck4( ) { //此处没有指明T来源于哪,默认来源于object,所以不能调用func1了 // t.Func1(); //如果上面不显示new(),此处编译不通过 t = new T(); } }
基类约束有两个功能:
(1)它允许在泛型类中使用由约束指定的基类所定义的成员。例如,可以调用基类的方法或者使用基类的属性。如果没有基类约束,编译器就无法知道某个类型实参拥有哪些成员。通过提供基类约束,编译器将知道所有的类型实参都拥有由指定基类所定义的成员。
(2)确保类型实参支持指定的基类类型参数。这意味着对于任意给定的基类约束,类型实参必须要么是基类本身,要么是派生于该基类的类,如果试图使用没有继承指定基类的类型实参,就会导致编译错误。
- 2、接口约束:
接口约束用于指定某个类型参数必须应用的接口。接口的两个主要功能和基类约束完全一样。有点类似基类约束。
基本形式 where T:interface-name
interface-name是接口的名称,可以通过使用由逗号分割的列表来同时指定多个接口。如果某个约束同时包含基类和接口,则先指定基类列表,再指定接口列表。
interface IA<T> { T Func1(); } interface IB { void Func2(); } interface IC<T> { T Func3(); } class MyClass<T, V> where T : IA<T> where V : IB, IC<V> { public MyClass(T t, V v) { //T的对象可以调用Func1 t.Func1(); //V的对象可以调用Func2和Func3 v.Func2(); v.Func3(); } }