1.前言
泛型(Generic)的优点主要体现在提升性能和类型安全两方面,下面将对这两点进行分析。
2.提升性能
通过编写一个使用 .NET 类库中 ArrayList 集合类的小程序,可体现出使用非泛型集合类的局限。 ArrayList 类的实例可以存储任何引用或值类型。
System.Collections.ArrayList list1 = new System.Collections.ArrayList();list1.Add(3);list1.Add(105);System.Collections.ArrayList list2 = new System.Collections.ArrayList();list2.Add("It is raining in Redmond.");list2.Add("It is snowing in the mountains.");
但这种便利有一定代价。 添加到 ArrayList 的任何引用或值类型均隐式向上转换为 Object。 如果项为值类型,将它们添加到列表时必须将其装箱,检索它们时必须取消装箱。 转换与装箱/取消装箱这两种操作都会降低性能;在必须循环访问大型集合的方案中,装箱与取消装箱的影响非常大。
下面创建一个的示例,查看两者的 IL 代码,示例代码如下:
class Test{ static void ArrayDemo() { ArrayList arrayList = new ArrayList(); arrayList.Add(12); Console.WriteLine(arrayList[0]); } static void GenericDemo() { List list = new List (); list.Add(12); Console.WriteLine(list[0]); } static void Main() { ArrayDemo(); GenericDemo(); }}
ArrayList 类型的 IL 代码如下:
.method private hidebysig static void ArrayDemo() cil managed{ // Code size 35 (0x23) .maxstack 2 .locals init ([0] class [mscorlib]System.Collections.ArrayList arrayList) IL_0000: nop IL_0001: newobj instance void [mscorlib]System.Collections.ArrayList::.ctor() IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldc.i4.s 12 IL_000a: box [mscorlib]System.Int32 IL_000f: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object) IL_0014: pop IL_0015: ldloc.0 IL_0016: ldc.i4.0 IL_0017: callvirt instance object [mscorlib]System.Collections.ArrayList::get_Item(int32) IL_001c: call void [mscorlib]System.Console::WriteLine(object) IL_0021: nop IL_0022: ret} // end of method Test::ArrayDemo
泛型 List<int> 的 IL 代码如下:
.method private hidebysig static void GenericDemo() cil managed{ // Code size 30 (0x1e) .maxstack 2 .locals init ([0] class [mscorlib]System.Collections.Generic.List`1list) IL_0000: nop IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1 ::.ctor() IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldc.i4.s 12 IL_000a: callvirt instance void class [mscorlib]System.Collections.Generic.List`1 ::Add(!0) IL_000f: nop IL_0010: ldloc.0 IL_0011: ldc.i4.0 IL_0012: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1 ::get_Item(int32) IL_0017: call void [mscorlib]System.Console::WriteLine(int32) IL_001c: nop IL_001d: ret} // end of method Test::GenericDemo
可以比较看出,通过创建泛型类型可以减少拆箱装箱的操作,从而提高性能。
3.类型安全
另一局限是缺少编译时类型检查;由于 ArrayList 将所有内容都转换为 Object,因此在编译时无法阻止客户端代码执行如下操作:
System.Collections.ArrayList list = new System.Collections.ArrayList();// Add an integer to the list.list.Add(3);// Add a string to the list. This will compile, but may cause an error later.list.Add("It is raining in Redmond.");int t = 0;// This causes an InvalidCastException to be returned.foreach (int x in list){ t += x;}
虽然在创建异类集合时这完全可以接受,甚至有时是有意为之,但将字符串和 ints 合并到单个 ArrayList 中更有可能属于编程错误,且此错误在运行时之前不会被检测出。
而在使用泛型类型的时候会使用占位符 <T> 为其指定类型,在创建实例后保证其类型安全。