首页 > 代码库 > 细说泛型

细说泛型

C#2的头号亮点 : 泛型

C#1中,Arraylist总是会给人带来困扰,因为它的参数类型是Object,这就让开发者无法把握集合中都有哪些类型的数据。如果对string类型的数据进行算术操作那自然会报错,但是遺憾的是在編譯期不会给你任何的提示

C#2中引入的泛型极其耀眼,甚至有些人会因为泛型而忽略C#2中其它新加入的特性

   

回到上面的问题,ArrayList带来的不仅仅是上面那些困扰,Object是所有类型的基类,但是我们在开发中它幾乎不会有它的身影,因为它包容一切,但是我们需要使用int做算术操作的时候就发生了问题,就是强制类型转换。当我们需要使用stringsubString方法IDE也不会给你任何提示,因为它是Object类型。

   

泛型带来的就是这样革命性的改变,允許你的参数类型为泛型类型。回到ArrayList,泛型的典型就是List<T>了,而T就是泛型类型,它是一个未綁定类型(不明确的)。但是在我们使用的时候需要进行指定

List<string> strList = new List<string>();

这时我们去访问它的元素时就会有string数据类型的智能提示,我们可以輕松的点出subString方法。这时 List<string>就是已构造 的泛型类型。而且因为是显示的,也不会有类型转换的问题。性能上也是大大的提高,这时我们把运行时需要检查的提前到了编译时

 

开放封闭类型

这是一个比较绕的概念,看名称比较迷糊其实很容易理解。

泛型类型虽然也是类型,而T就是泛型类型,但它是开放的,可以是string,int,class等。开放类型无法创建实例,因为不知道创建什么实例,而封闭类型可以创建实例,封装类型就是指定了实际的数据类型,比如<string>

   

泛型约束

泛型类型也不总是任意数据类型都可以的,在开发的时候我们常常需要对传入的实际类型进行把握,如果外部使用时传入了意外的类型,那么就会引发BUG

引用类型约束

如果我们希望数据类型是引用类型,那么我们可以 where T: class

值类型约束

如果我们希望数据类型是值 类型,那么我们可以 where T: struct

構造函數约束

我们希望数据类型是拥有无参的构造函数,那么我们可以 where T: new()

转换类型约束

我们希望数据类型是可以转换为我们想要指定的数据类型,比如某个基类的子类,某个接口的实现。我们可以 where T: Person

我们还可以根据实际需要进行组合约束,但是需要注意的是不是所有的约束都可以组合在一起,比如 约束不可以既是引用类型约束又是值类型约束。

   

泛型方法类型实参的类型推断

需要注意的是,这并不是我们常用的var类型推断,而是泛型方法的类型推断。

比如list<string>.Add("小明"); 这时小明很明显是一个字符串,那么这段代码就可以简化成 list.Add("小明");

   

高级泛型

这部分主要讨论的是泛型的效率问题与反射部分

都知道,相比较arrayList,泛型不会有拆箱装箱的过程,除了这些他们在内存中存儲的方式也是不一样的。比如ArrayListbyteList<byte>ArrayList会为每一个字节进行装箱并存儲已装箱值的引用。而List<byte>没有額外的引用存儲。他们看起来如下图

技术分享

   

对于静态类型与静态字段还有静态構造函數, 我们都知道他们只会初始化一次,并且之后不会发生改变。对于泛型来说同一个泛型参数类型确实如此 ,每个封闭类型都有自己的静态字段集。它们是独立的。

比如 List<int> a;

List<string> b;

List<object > c;

List<int> d;

我们可以看到ad是一个泛型参数类型,它们是静态构造函數只会运行一次,但是bca会走三次,因为类型不一样。

   

JIT编译器处理泛型的时候,会把值类型翻译成不同的本地码,而引用类型翻译成共享的本地码。因为引用具有相同的大小。而值类型并不是。

   

我们在使用foreach的时候会感到很便利,有时候我们也会自己实现一个迭代器,这很简单,只要去实现IEnumerable<T>接口与IEnumerator<T>接口即可,但是如果我们发现泛型的接口又实现着非泛型的接口。如果没有问题,泛型接口都应该继承一个对应的非泛型接口,这样就可以实现协变性,在之前的C#版本中也会适用。

   

   

泛型存在的不足

C#的泛型设计的非常的巧妙,但是也有一些存在的不足之处。

   

泛型可变性(协变、逆变)的缺乏

在面向对象开发中我们可以用子类去New基类,这是没有问题的,但是对于泛型来说是不可以的,可以看到代码第一行是报错的。泛型不支持可变性,它们是不变体,这是为了类型安全着想

技术分享

   

但是我们可以通过其它方法来实现可变性。最简单的使用泛型接口,和编写輔助类。在4.0中有了其它的解决方法

   

缺乏操作符约束

我们想要对泛型实参进行算术运算,但是并不是所有的类型都可以进行算术运行的。也没有类似于 + - / *这种操作符的约束。我们可以使用表达式树和动态特性解决此问题

   

缺乏泛型属性、索引器、成员类型

这很直观 ,上面所说的都无法使用泛型,我们不可以

Public T Property<T>{get;set;}

当然这个是无解的。因为至少我们开发的时候不会有这样的需求

细说泛型