所谓泛型,即通过参数化类型来实现在同一份代码上操作多种数据类型,泛型编程是一种编程范式,它利用“参数化类型”将类型抽象化,从而实现更灵活的复用。
C#泛型赋予了代码更强的类型安全,更好的服用,更高的效率,更清晰的约束。
机制:
C#泛型能力是由CLR在运行时支持的,区别于C++的编译时模板机制,和Java的编译时的“搽拭法”。这使得泛型能力可以在各种支持CLR语言之间进行无缝的互操作。
C#泛型代码在被编译为IL代码和无数据时,采用特殊的占位符来表示泛型机制,并用转悠的IL指令支持泛型操作,而真正的泛型实例化以“on-demand”的方式,发生在JIT编译时
C#泛型编译机制:
第一轮编译时,编译器只为Stack<T>(栈算法)类型产生“泛型版”的IL代码与元数据—并不进行泛型的实例化,T在中间只充当占位符。
JIT编译时,当JIT编译器第一次遇到Stack<int>时,将int替换“泛型版”IL代码与元数据中的T—进行泛型类型的实例化。
CLR为所有类型参数为“引用类型”的泛型类型产生同一份代码;但是如果类型参数为“值类型”,对没一个不同的“值类型”,CLR将为其产生一份独立的代码。
C#泛型的特点
如果实例化泛型类型的参数相同,那么JIT编译器会重复使用该类型,因此c#的动态泛型能力避免了c++静态模板可能导致的代码膨胀的问题;
C#泛型类型携带有丰富的元数据,因此C# 的泛型类型可以应用于强大的反射机制
C# 的泛型类型采用“基类、接口、构造器、值类型、引用类型”的约束方式来实现对类型能数的“显式约束“,提高了类型安全的同时,也丧失了c++模板基于“签名”的隐式所具有的高灵活性
泛型类型的成员
泛型类型的成员可以使用泛型类型声明中的类型参数,但是类型参数如果没有任何约束,则只能在该类型上使用从System.Object继承的共有成员
泛型接口
泛型接口的类型参数要么实例化,要么来源于实现类声明的类型参数
泛型委托
Delegate bool Predicate<T>(T value)
Class x{
Static bool F(int i){}
Static bool G(string s){}
Static void Main()
{
Predicate<string> p2=G;
Predicate<inr> p1=new Predicate<Int> (F);
}
}
泛型委托支持在委托返回值和参数上应用参数类型,这些参数类型同样可以附带合法的约束
泛型方法简介
C#泛型机制只支持“在方法声明上包含类型参数”—泛型方法
泛型机制不支持在除方法外的其他成员(属性、事件、所引起、构造器、析构器)的声明上包含类型参数,但是这些成员本身可以包含在泛型类型中,并使用泛型类型的类型参数
泛型方法既可以包含在泛型类型中,也可以包含在非泛型类型中
Public class Finder{
Public static int Find<T>(T[]items,T item){
For(int i=0;i<items.Legth;i++)
{
If(--)return I;
}
Return -1;
}
}
泛型方法调用
Int i=Finder.Find<int>(new int[]{1,2,3,4,5,},6);
泛型方法的重载
Class MyClass
{
Void F1<T>(T[] a,int i);//不可以构成重载方法
Void F1<U>(U[] a,int i)
Void F2<T>(int x);可以构成重载方法
Void F2(int x);
Void F3<T>(T t)where T:A
Void F3<T>(T t)where T:B
}
泛型方法的重写
Abstract class base
{
Public abstract T F<T,U>(T t,U u) where U:T;
Public abstract T G<T>where U:ICompareable
}
Class Derived:Base
{
//合法的重写,约束被默认继承
Public override X F<X x,Y y>{}
//非法的重写,指定的任何约束都是多余的
Public override T G<T t> where T:Compareable(){}
}
泛型约束简介
C#泛型要求对“所有泛型类型或泛型方法的类型参数”的任何假定,都要基于“显示的约束”,以维护C# 所要求的类型安全
“显示约束”由where子句表达,可以指定“基类约束”、“接口约束”、“构造器约束”、“值类型/引用类型约束”供4中约束
“显示约束”并非必须,如果没有指定“显示约束”,泛型类型参数将只能方法System.Object类型中的共有方法
以下是4中约束:
构造器约束
Class A{public A(){}}
Calss B{public B(int i){}}
Class C<T> where T:new()
{
//可以在其中使用T t=new T();
}
C<A> c=new C<A>();//可以;A有无参构造器,必须有一个却省的默认值
C<B> c=new C<B>();//错误,B没有无参构造器
值类型/引用类型约束
Public struct A{}
Public class B{}
Clss C<T> where T:struct
{
//T在这里是一个值类型
}
C<A> c=new C<A>();//可以;A是一个值类型
C<B> c=new C<B>();错误,B是一个引用类型
基于约束
基类约束
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的变量上条用F1
//可以在类型为T的变量上调用F2
}
接口约束
Interface IPrintable{ void Pint();}
Interface ICompareable<T>{int CompareTo(T v);}
Interface IKeyProvider<T>{T GetKey();}
Class Dicationary<K,V>
Where K:ICompareable<K>
Where V:IPrintable,IKeyProvider<K>
{
//可以在类型为K的变量上调用CompareTO
//可以在类型为V的变量上调用Pint和GetKey
}
泛型的设计是用来减少你重复实现代码的次数-只需要改变数据类型即可。因为抽象数据结构,如队列、栈和列表皆是典型的数据结构,所以存在针对这些东西的泛型类型完全可以理解,你可以从.Net中派生大量的值-通过使用现有的泛型类型。
泛型的意义
泛型的意义何在?类型安全和减少装箱、拆箱并不是泛型的意义,而是泛型带来的两个好处而已(或许在.net泛型中,这就是明显的好处)泛型的意义在于—把类型作为参数,它实现了代码见的很好的横向联系,我们知道继承为了代码提供了一种从上往下的纵向联系,单泛型提供了方便的横向联系(从某种程度上说,它和AOP在思想上有相同之处)