C#基础系列—泛型(Generics)(上)
(原创-- 转载请注明huwz)
这是一篇关于C#泛型介绍的文章,除了基本的概念性介绍之外,我会加一些我自己的理解和我的想法。
下面我主要从这四个方面来谈谈C#泛型:
l 什么是泛型
l 泛型的优点
l 常用泛型介绍
l 设计泛型
什么是泛型
先一起看看官方(MSDN)怎么说:
Generics are classes, structures, interfaces and methods that have placeholders(type parameters) for one or more of the types they store or use.
泛型是带有一个或多个占位符(类型参数)的类,结构,接口或方法,这些占位符被它们保留或者使用。
举个例子可能说的更明白一点:
Class MyClass<T>
{
public T amember;
public void Method<T>(T obj){ }
}
这里我们有一个泛型的类和一个泛型的方法,<T>表示的就是占位符(placeholder),但是T只能表示一种类型——任意的一种类型,可以是值类型,可以是引用类型,比如int, string, DateTime等等。在使用的时候,一旦指定了这个类型,那么就不允许更改了。
举个例子说,我们在某处有这么一行
MyClass<int> myObj = new MyClass<int>();
那么我们可以把这个类的定义想像成
Class MyClass<int>
{
public int amember;
public void Method<int>(int obj){ }
}
就是把T替换成了int,再简单不过了。
这里T灵活的表示了一个类型。如果我们需要支持多个类型,以两个类型为例,该如何写呢?
是 Class MyClass<T,U>
还是Class MyClass<T><U> ?
这个我们留在后面设计泛型的时候再来讨论。不过相信大家已经有了正确的判断。至于T,U,为什么用这两个字母,似乎这个没有规定,你用X,Y,Z也行;随便用什么字母都行,只要不是某种特定类型的,它都可以用作泛型。也不限定一个字母,两个字母,比如<Tu>,它也可以作泛型。
说了那么多,希望帮助大家理解什么是泛型。关于泛型类型的编译,有兴趣的可以看看下面这段我从别人那摘录的一段话。不明白也没关系,可以接着看下面的泛型的优点。
C#泛型编译机制
第一轮编译时,编译器只为Stack<T>类型产生“泛型版”的IL代码和元数据,并不进行泛型类型的实例化,T在中间只充当占位符。
JIT编译时,当JIT编译器第一次遇到Stack<int>时,将用int类型替换“泛型版”IL代码与元数据中的T -- 进行泛型类型的实例化。
CLR为所有类型参数为“引用类型”的泛型类型产生同一份代码,但如果类型参数为“值类型”,对每一个不同的“值类型”,CLR将为其产生一份独立的代码。
|
泛型的优点
l 提高性能
l 编译前检查
这是使用泛型最直接的好处。
什么时候能提高性能呢?
大家看看这段代码






































这里有两个方法,它们都是做一件事情,将1000个数放到一个集合中,然后再从中读取出来;区别是UseGeneric()方法使用的是泛型集合,而NotUseGenerics()使用的是普通集合。我们知道ArrayList里的个体都是Object类型的,所以将值类型放进去的时候,必然有个装箱过程(Box),在读取数据的时候,又有个拆箱的过程(UnBox)。
无论是拆箱还是装箱,都会带来额外的开销,而使用泛型则可以避免这种开销。因此在这种情况下,可以带来性能上的提高。
那编译前检查又有什么好处呢?
我们再看一段代码。



















这里还是做一个事情的两个方法,但是在编译的时候,UseGeneric()方法会报错:
The best overloaded method match for 'System.Collections.Generic.List<string>.Add(string)' has some invalid arguments
因为我们试图把一个整型值加进去。
而NotUseGeneric()方法却无动于衷。这样会带来什么样的不好呢?
进去容易出来难啊!因为ArrayList中的个体都是object类型的,我们无法判断哪一个是int,哪一个是string,所以也就很容易出现类型转换的错误。这样我们就把代码中的风险带到了运行时,这是很要命的。就像是你过年回家好不容易买到一张票,结果半道上查票的时候发现你买的是假票,把你赶下火车;而使用泛型就像是你买票的时候就帮你鉴别票的真伪,上车查票的时候你就放心多了。
常用泛型介绍
泛型类最常用于集合,如链接列表,哈希表,堆栈,队列,树等,其中,像从集合中添加和移除项这样的操作都以大体上相同的方式执行,与所存储数据的类型无关。
|
还有数据类型,比如int,double,它们都可以加减乘除——与类型无关但执行方式相同。
List<> 和 ArrayList
Dictionary<,> 和HashTable