2014-06-15
初始泛型
12.3 泛型基础结构
12.3.1 开放类型与封闭类型
12.3.2 泛型类型和继承
12.3.3 泛型类型同一性
12.3.4 代码爆炸
参考
泛型(generic)是CLR和编程语言提供一种特殊机制,它支持另一种形式的代码重用,即"算法重用"。
简单地说,开发人员先定义好一个算法,比如排序、搜索、交换等。但是定义算法的开发人员并不设定该算法要操作什么数据类型;该算法可广泛地应用于不同类型的对象。然后,另一个开发人员只要指定了算法要操作的具体数据类型,就可以使用这个现成的算法了。
泛型有两种表现形式:泛型类型和泛型方法。
以最常用的FCL中的泛型List<T >为例:
1 using System.Collections.Generic; 2 3 namespace Generics 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 List<int> num = new List<int>(); 10 num.Add(1); 11 num.Add(3); 12 int num1 = num[0]; 13 int num2 = num[1]; 14 } 15 } 16 }
尖括号中的T是不确定的数据类型,叫做类型参数(type parameter),一般规定以字母T开头,可以是TKey, TValue都可以。而调用时指定的具体类型叫做类型实参(type argument)。
类型参数是真实类型的占位符。
为了是泛型能够工作,Microsoft必须完成以下工作:
开放类型:具有泛型参数的类型是开放类型,如List<T>,CLR不允许构造开放类型的实例;
封闭类型:在实际调用代码时,如果所有类型实参都已经指定了实际数据类型,如List<string>,则该类型为封闭类型。CLR允许构造封闭类型的实例。
泛型类型仍然是类型,所以它能从其他任何类型派生。使用一个泛型类型并指定类型实参时,实际上是在CLR中定义一个新的类型对象,新的类型对象是从派生该泛型类型的那个类型派生的。也就是说,由于List<T>是从Object派生的,那么List<String>和List<Guid>也是从Object派生的。
有的时候,泛型语法会将开发人员搞糊涂,所以有的开发人员定义了一个新的非泛型类类型,它从一个泛型类型派生,并指定了所有的类型实参。例如,为了简化一下代码:
List<DateTime> dt = new List<DateTime>();
一些开发人员可能首先定义下面这样的一个类:
1 internal sealed class DateTimeList : List<DataTime> 2 { 3 //这里无需放任何代码! 4 }
然后就比较一下DateTimeList和List<DateTime>的同一性:
1 static void Main(string[] args) 2 { 3 DateTimeList dt = new DateTimeList(); 4 Boolean sameType = (typeof(List<DateTime>) == (typeof(DateTimeList))); 5 }
上述代码运行时,sameType会初始化为false,因为比较的是两个不同类型的对象。
因此,决不要单纯处于增强源代码的易读性类这样定义一个新类。这样会丧失类型同一性(identity)和相等性(equivalence)。也就是说,假如一个方法的原型接受一个DateTimeList,那么不能将一个List<DateTime>传给它。然而,如果方法的原型接受一个List<DateTime>,那么可以将一个DateTimeList传给它,因为DateTimeList是从List<DateTime>派生的。
使用泛型类型参数的一个方法在进行JIT编译时,CLR获取方法的IL,用指定的类型实参进行替换,然后创建恰当的本地代码。然而,这样做有一个缺点:CLR要为每种不同的方法/类型组合生成本地代码。我们将这个现象称为"代码爆炸"。它可能造成引用程序集的显著增大,从而影响性能。
CLR内建了一些优化措施,能缓解代码爆炸。首先,假如为一个特定的类型实参调用了一个方法,以后再次使用相同的类型实参来调用这个方法,CLR只会为这个方法/类型组合编译一次。所以,如果一个程序集使用List<DateTime>,一个完全不同的程序集也使用List<DateTime>,CLR只会为List<DateTime>编译一次方法。
CLR还提供了一个优化措施,它认为所有引用类型实参都是完全相同的,所以代码能够共享。之所以能这样,是因为所有引用类型的实参或变量时间只是执行堆上的对象的指针,而对象指针全部是以相同的方式操作的。
但是,假如某个类型实参是值类型,CLR就必须专门为那个值类型生成本地代码。因为值类型的大小不定。即使类型、大小相同,CLR仍然无法共享代码,可能需要用不同的本地CPU指令操作这些值。
[1] 跟小静读CLR via C#(16)--泛型 http://www.cnblogs.com/janes/archive/2011/12/21/2295959.html
[2] 《CLR via C#》读书笔记(7) -- 泛型(上) http://www.cnblogs.com/Code-life/archive/2012/12/20/2823520.html
《CLR via C#》读书笔记 之 泛型,布布扣,bubuko.com
原文:http://www.cnblogs.com/Ming8006/p/3789847.html