通过允许指定泛型类或方法操作的特定类型,泛型功能将类型安全的任务从您转移给了编译器。不需要编写代码来检测数据类型是否正确,因为会在编译时强制使用正确的数据类型。减少了类型强制转换的需要和运行时错误的可能性;c#泛型的优点和缺点,如下所示: |
1.性能 |
|
|
|
|
例子显示了System.Collections名称空间中的ArrayList类,该类存储对象,Add方法定义为需要把一个对象作为参数,所以要装箱一个整数类型,在读取ArrayList中的值时,要进行拆箱,把对象转换为整数类型,代码如下: |
|
var list=new ArrayList(); |
|
|
list.Add(12); //boxing --- convert a value type to a reference type |
|
int i1=(int)list[0]; //unboxing --- conver a reference type to a value type |
|
foreach(int i2 in list) |
|
|
|
{ |
|
|
|
|
Console.WriteLine(i2); //unboxing |
|
} |
|
|
|
装箱和拆箱操作很容易使用,但性能损失比较大,遍历许多项时尤其如此。 |
System.Collections.Generic名称空间中的List<T>类不使用对象,而是在使用时定义类型,在下面例子中,List<T>类的泛型类型定义为int,所以int类型在JIT编译器动态生成的类中使用,不再进行装箱和拆箱操作: |
|
var list=new List<int>(); |
|
|
list.Add(12); //no boxing --- value types are stored in the List<int> |
|
int i1=list[0]; //no unboxing,no cast needed |
|
foreach(int i2 in list) |
|
|
|
{ Console.WriteLine(i2); } |
|
2.类型安全性 |
|
|
|
泛型的另一个特性是类型安全,与ArrayList类一样,如果使用对象,就可以在这个集合中添加任意类型,下面的例子在ArrayList类型的集合中添加一个整数,一个字符串和一个MyClass类型的对象。 |
|
var list=new ArrayList(); |
|
|
list.Add(12); |
|
|
|
list.Add("mystring"); |
|
|
|
list.Add(new MyClass()); |
|
如果这个集合使用下面的foreach语句迭代,而该foreach语句使用整数元素来迭代,编译器就会编译这段代码,但并不是集合中的所有元素都可以强制转换为int,所以会出现一个运行异常; |
|
foreach(int i2 in list) |
|
|
|
{ Console.WriteLine(i2); } |
|
错误应尽早发现,在泛型类List<T>中,泛型类型T定义了允许使用的类型,有了List<int>的定义,就只能把整数类型添加到集合中,编译器就不会编译这段代码,因为Add()方法的参数无效。 |
|
var list=new List<int>(); |
|
|
list.Add(12); |
|
|
|
list.Add("mystring"); //compile time error |
|
list.Add(new MyClass()); //compile time error |
|
|
|
|
|
3.二进制代码重用 |
|
|
|
泛型允许更好的重用二进制代码,泛型类可以定义一次,并且可以用许多不同的类型实例化,不需要像C++模板那样访问源代码 |
例如,System.Collections.Generic名称空间中的List<T>类用一个int、一个字符串和一个MyClass类型实例化: |
|
var list=new List<int>(); |
|
|
list.Add(12); |
|
|
|
var stringList=new List<string>(); |
|
|
stringList.Add("mystring"); |
|
|
var myClassList=new List<MyClass>(); |
|
myClassList.Add(new MyClass()); |
|
泛型类型可以在一种语言中定义,在任何其他.NET语言中使用。 |
|
|
|
|
|
4.代码的扩展 |
|
|
|
在用不同的特定类型实例化泛型时,会创建多少代码? |
因为泛型定义会放在程序集中,所以用特定类型实例化泛型类不会在IL代码中复制这些类,但是,在JIT编译器把泛型类编译为本地代码时, |
会给每个值类型创建一个新类,引用类型共享一个本地类的所有相同的实现代码,这是因为引用类型在实例化的泛型类中只需要4个字节 |
的内存地址(32位系统),就可以引用一个引用类型,值类型包含在实例化的泛型类的内存中,同时因为每个值类型对内存的要求都不同,所以 |
要为每个值类型实例化一个新类, |
|
|
|
|
|
|
5.命名约定 |
|
|
|
如果在程序中使用泛型,在区分泛型类型和非泛型类型时候就会有一定的帮助,下面是泛型类型的命名规则: |
|
1.泛型类型的名称用字母T作为前缀 |
|
2.如果没有特殊的要求,泛型类型允许用任意类代替,且只使用了一个泛型类型,就可以用字符T作为泛型类型的名称 |
|
public class List<T>{ } |
|
|
|
public class LinkedList<T>{ } |
|
|
3.如果泛型类型有特定的要求(例如,它必须实现一个借口或派生自基类),或者使用了两个或者多个泛型类型,就应给泛型类型使用描述性的名称。 |
|
public delegate void EventHandler<TEventArgs>(object sender,TEventArgs e); |
|
public delegate Toutput Converter<Tinput,Toutput>(Tinput from); |
|
public class SortedList<Tkey,Tvalue>{ } |