泛型

JIT编译器如何处理泛型

使用泛型类型参数的一个方法在进行JIT编译时,CLR获取方法的IL,使用指定的类型实参进行替换,然后创建恰当的本地代码。JIT为每个值类型实参(int, long, float)都创建不同的本地代码,为引用类型(string, stream)共享相同的本地代码。之所以可以这样是因为所有引用都具有相同的大小,对其操作也是一样的。

优点

想知道泛型的优点可以对比ArrayList和List<T>集合,ArrayList集合接受的参数是object类型,对于值类型需要装箱操作,如果需要还得拆箱,这会造成性能丢失和拆箱可能抛出异常,对于引用类型都会转换成object类型。如果代码如下:

ArrayList list = new ArrayList();

list.Add(2);

list.Add("abc");

然后

int sum = 0;

foreach(int l in list)

{

     sum += l;

}

这代码可以通过编译,但是在运行时出错,我们知道尽可能的在编译时期检查出错误,如果使用泛型List<T>集合

List<int> list = new List<int>();

list.Add("abc");

会编译通不过,因此泛型在编译时期增强了类型的安全性。

继续上述的例子,使用ArrayList的Add方法添加值类型,会发生装箱操作,而List<T>则不用,因此泛型提升了性能。

使用泛型减少了类型转换的次数,使代码的更清晰。

泛型类型和泛型方法

泛型类型包括泛型类,泛型委托和泛型接口

泛型方法:

类型推断:

假如有

public static void Display(string str);

public static void Display<T>(T t);

调用时

Display("alab");     // 调用第一个,因为编译器会选择更明确的匹配,调用Dsiplay(string str)

Display(2);             // 类型推断,与Display<int>(2)等价

Display<int>(2);

Display<string>("alab");  // 明确指出,调用的是Display<T>(T t)

约束:

泛型类型和泛型方法都支持约束,比如

private static T Min<T>(T o1, T o2)

{

   if (o1.CompareTo(o2) < 0) ) return o1;

   return o2;

}

这个是编译不过的,因为许多类型没有提供CompareTo方法,如果这样,泛型是在弱爆了,还好有约束,可以限定类型实参的类型数量,我们只需private static T Min<T>(T o1, T o2) where T:IComparable<T>即可,限定类型参数必须实现IComparable接口。

约束分为3种:

1. 主要约束:class, struct;分别约束为引用类型和值类型,但是引用类型有一些特例,比如System.Object等是不行的。比如约束为class之后可以设定T t = null了。必须兼容引用类型,比如Stream,要么是Stream,好么就是继承于Stream

2. 次要约束:接口,类型参数;即实现接口,兼容类型参数约束

3. 构造器约束:new() 表示类型参数必须实现了一个公共无参数的构造器,会和struct起冲突,因为struct默认就有公共无参数的构造器。

泛型委托和接口的逆变和协变泛型类型参数

逆变量:泛型类型实参可以从一个基类更改为该类的派生类,使用in关键字修饰,用于函数参数

协变量:泛型类型实参可以从一个类更改为其基类,使用out关键字修饰,用于函数返回值

支持逆变性和协变性使定义的委托可以再更多的情形下使用。

我们分析一下其合理性,假如有一个委托类型定义

public delegate TResult Func<in T, out TResult>(T arg);

委托变量 Func<MemoryStream, object> fn1 = null;

那么函数 public object Fun1(MemoryStream o)肯定匹配委托变量fn1;

public string Fun2(Stream s)也是匹配的。

定义泛型委托实例的时候可以把形参定义的范围小一点,返回值大一点。

image

posted @ 2012-09-20 19:42  alab  阅读(260)  评论(0编辑  收藏  举报