面向对象编程一个好处就是“代码重用”,极大提升了开发效率。如是,能够派生出一个类,让它继承基类的全部能力,派生类只须要重写虚方法,或添加一些新的方法,就能够定制派生类的行为,使之知足开发人员的需求。
泛型(generic)是CLR和编程语言提供的一种特殊机制,它支持另外一种形式的代码重用,即“算法重用”。
简单地说,开发人员先定义好一个算法,好比排序、搜索、交换、比较或转换等。可是,定义算法的开发人员并不设定该算法要操做什么数据类型;该算法能够普遍地应用于不一样类型的对象。而后,另外一个开发人员只要指定了算法要操做的具体数据类型,就可使用这个现成的算法了。例如,可用一个排序算法来操做Int32和String等类型的对象,或用一个比较算法来操做DateTime和Version等类型的对象。大多数算法都封装在一个类型中,CLR容许建立泛型引用类型和泛型值类型,但不容许建立泛型枚举类型。还容许建立泛型接口和泛型委托。
泛型为开发人员提供了一下优点:
#1,源代码保护。
#2,类型安全。编译器和CLR能理解开发人员的意图,并保证只有与指定数据类型兼容的对象才能随同算法使用。
#3,更加清晰的代码。减小了源代码中必须进行的转型次数,代码更容易编写和维护。
#4,更佳的性能。建立泛型算法来操做一种具体的值类型,因此值类型的实例可以以传值方式传递,CLR再也不须要执行任何装箱操做。ArrayList来操做值类型(如Int32),会形成大量装箱操做,大量的垃圾回收。
泛型最明显的应用就是集合类。FCL已经定义了几个泛型集合类,其中,大多数类都在System.Collections.Generic 和System.Collections.ObjectModel命名空间中。
泛型基础结构
开发类型和封闭类型
咱们关注CLR如何为应用程序使用的每一个类型建立一个怎样的内部数据结构,这种数据结构称为类型对象(type object)。具备泛型类型参数的类型仍然是类型,CLR一样会为它建立一个内部类型对象。不管引用类型(类)、值类型(结构)、接口类型,仍是委托类型,这一点都成立。具备泛型类型参数的类型称为
开放类型(open type), CLR禁止构造开发类型的任何实例,相似CLR禁止构造接口类型的实例, 如List<>。
代码引用一个泛型类型时,可指定一组泛型类型实参。假如为全部类型实参传递的都是实际数据类型,类型就称为
封闭类型(closed type),CLR容许构造封闭类型的实例,如List<String> 。须要注意的是,CLR会在类型对象内部分配类型的静态字段,所以每一个封闭类型都有本身的静态字段。例如,List<T>定义了任何静态字段,都不会在一个List<DateTime>和一个List<String>之间共享:每一个封闭类型对象都有它本身的静态字段。假如一个泛型类型定义了一个静态构造器,那么针对每一个封闭类型,这个构造器都会执行一次。在泛型类型上定义一个静态构造器的目的是保证传递的类型实参知足特定的条件(约束)。如,但愿一个泛型类型只用于处理枚举类型,就能够以下定义:
internal sealed class GenericTypeThatRequiresAnEnum<T> {
static GenericTypeThatRequiresAnEnum() {
if(!typeof(T).IsEnum) {
throw new ArgumentException("T must be an enmuerated type");
}
}
}
CLR提供了一个名为约束的功能,可利用它更好地定义一个泛型类型来支出哪些类型实参是有效的。
代码爆炸
使用泛型类型参数的一个方法在进行JIT编译时,CLR获取方法的IL,用指定的类型实参进行替换,而后建立恰当的本地代码(这些代码是为操做指定数据类型的方法“量身定制”的)。这样作有一个缺点:CLR要为每一种不一样的方法/类型组合生成本地代码。这个现象称为
代码爆炸(code explosion)。它可能形成应用程序的工做集显著增大,从而损害性能。
CLR其实内建了一些优化措施,可以缓解代码爆炸。假如为一个特定的类型实参调用一个方法,之后再次使用相同的类型实参来调用这个方法。CLR只会为这个方法/类型组合编译一次代码。若是一个程序集使用List<DateTime>, 一个彻底不一样的程序集(加载到一个AppDomain中)也使用List<DateTime>, CLR只会为List<DateTime>编译一次方法。这样显著缓解了代码爆炸。
CLR还提供了另外一个优化措施,它认为全部引用类型实参都是彻底相同的,因此代码能够共享。如,CLR为List<String>的方法编译的代码可直接用于List<Stream>的方法,由于String和Stream均为引用类型。事实上,对于任何引用类型,都会使用相同的代码。CLR之因此能执行这个优化,是由于全部引用类型的实参或变量实际只是指向堆上的对象的指针,而对象指针所有是以相同的方式来操做的。
可是,某个类型实参是值类型,CLR就必须专门为这个值类型生成本地代码,由于值类型的大小不定,还可能要用不一样的本地CPU指令来操纵这些值。
委托和接口的逆变和协变泛型类型实参