在学习设计模式的时候,我们用到了范型,在VB.NET机房重构的时候,我们又用到了范型,感觉范型就像一个集合Collections,而且集合ArrayList()非常好用,它可以盛放许多类型的数据(里面涉及到了拆箱与装箱),那么,范型与ArrayList()又有什么渊源哪?下面我讲一下C#中的范型与集合ArrayList()的区别。
一、泛型简介:
泛型(Generic Type)是.NET Framework2.0最强大的功能之一。泛型的主要思想是将算法与数据结构完全分离开,使得一次定义的算法能作用于多种数据结构,从而实现高度可重用的开发。通过泛型可以定义类型安全的数据结构,而没有必要使用实际的数据类型,这将显著提高系统性能并得到高质量的代码(因为可以重用数据处理算法,没有必要复制类型特定的代码。
二、范型与ArrayList()的渊源关系
拿C#编程为例,C# 是一个类型安全的语言,类型安全允许编译器(可信赖地)捕获潜在的错误,而不是在程序运行时才发现(不可信赖地,往往发生在你将产品出售了以后!)。因此,在C#中,所有的变量都有一个定义了的类型;当你将一个对象赋值给那个变量的时候,编译器检查这个赋值是否正确,如果有问题,将会给出错误信息。
在.Net 1.1 版本(2003)中,当你在使用集合时,这种类型安全就失效了。由.Net 类库提供的所有关于集合的类全是用来存储基类型(Object)的,而.Net 中所有的一切都是由Object基类继承下来的,因此所有类型都可以放到一个集合中。于是,相当于根本就没有了类型检测。更糟的是,每一次你从集合中取出一个Object,你都必须将它强制转换成正确的类型(拆装箱)这一转换将对性能造成影响,并且产生冗长的代码(如果你忘了进行转换,将会抛出异常。更进一步地讲,如果你给集合中添加一个值类型(比如,一个整型变量),这个整型变量就被隐式地装箱了(再一次降低了性能),而当你从集合中取出它的时候,又会进行一次显式地拆箱(又一次性能的降低和类型转换)。
在公共语言运行库和C# 语言的早期版本中,通用化是通过在类型与通用基类型Object 之间进行强制转换来实现的,泛型提供了针对这种限制的解决方案。通过创建泛型类,您可以创建一个在编译时类型安全的集合。使用非泛型集合类的限制可以通过编写一小段程序来演示,该程序利用.NET Framework 基类库中的ArrayList 集合类。ArrayList 是一个使用起来非常方便的集合类,无需进行修改即可用来存储任何引用或值类型。
个人理解:当我们用C#或者VB.NET编写的代码想让机器执行时,是一种高级语言向低级语言转化的过程,由C#编写的代码转化成机器语言,这样机器才可以直接执行。执行的过程是:编译器将源码转化为微软中间语言MSIL,然后再由公共语言运行时(CLR)转化成机器语言。(如果不理解,可以点击此超链接)这样,机器就可以直接执行我们编写的代码了。如果不理解,可以看我的博客.NET框架。当编译器对源码进行编译时,就实现了上面所说的在编译阶段检查错误的功能。
范型与集合相比,有以下几个优点。
1、性能
对值类型使用非泛型集合类,在把值类型转换为引用类型,和把引用类型转换为值类型时,需要进行装箱和拆箱操作。装箱和拆箱的操作很容易实现,但是性能损失较大。假如使用泛型,就可以避免装箱和拆箱操作。
ArrayList list=new ArrayList(); list.Add(20); //装箱,list存放的是object类型元素,须将值类型转化为引用类型 int i=(int)list[0]; //拆箱,list[0]的类型是object,要赋值就得把引用类型转化为值类型
如果换成泛型编程,就不会有装箱和拆箱的性能损失。
List<T> list=new List<int>(); list.Add(20); //因为指定了用int来实例化,因此不必装箱 int i=list[0]; //同样地,访问时也不需要拆箱,可以直接接受,不需要转化。
2、类型安全。
与ArrayList类一样,如果使用对象,可以在这个集合中添加任意类型。如果使用非泛型编程,如下代码,就有可能在某些情况下会发生异常。注意:编译时不出错,但运行时有错。
ArrayList list=new ArrayList(); list.Add(20); list.Add("string"); foreach(int i in list) { Console.WriteLine(i); //这里会有个异常,因为并不是集合中的所有元素都可以转化为int }
如果该用泛型编程,则可以避免这种异常,让编译器检查出错误。
List<int> list=new List<int>(); list.Add(20); lsit.Add("string"); //编译时报错,只能报整数类型添加到集合中
泛型可以定义一次,用许多不同的类型实例化,不需要像C++模板那样访问源代码。泛型可以在一种语言中定义,在另一种.NET语言中使用。
4、代码的扩展
因为泛型类的定义会放在程序集中,所以用某个类型实例化泛型泛型类不会在IL(微软中间语言)代码中复制这些类。但是,在JIT编译器把泛型类编译为内部代码时,会给每个值类型创建一个新类。引用类型共享同一个内部类的所有实现代码。这是因为引用类型在实例化的泛型类中只需要4字节的内存单元(32位系统),就可以引用一个引用类型。值类型包含在实例化的泛型类的内存中。而每个值类型对内存的要求都不同,所以要为每个值类型实例化一个新类。
以上是本人对范型和集合做了一点点的比较,总的来说,利用范型可以定义类型安全的数据结构(编译器编译时检查错误),能提高代码的性能(减少拆装箱) ,可以复用,当然,代码扩展。