1、 泛型(泛型(Generic)是)是CLR 2.0相对于相对于CLR 1.0新增的一个特新增的一个特性。在此前的性。在此前的CLR 1.0中,如果要创建一个灵活的类或方中,如果要创建一个灵活的类或方法,并且该类或方法需要在代码编译后才能确定类型,那法,并且该类或方法需要在代码编译后才能确定类型,那么就必须以么就必须以Object类为基础。但是类为基础。但是Object类在编译期间类在编译期间是无法保证类型安全的,这是因为任何类型都可以转换为是无法保证类型安全的,这是因为任何类型都可以转换为Object类。然而类。然而Object类转换为其他类型都将使用强制类转换为其他类型都将使用强制类型转换的
2、方法,这就容易造成类型转换错误,且将值类类型转换的方法,这就容易造成类型转换错误,且将值类型转换为型转换为Object类会为造成性能损失。类会为造成性能损失。针对上述问题,针对上述问题,CLR 2.0提供了泛型。有了泛型,就不需提供了泛型。有了泛型,就不需要使用要使用Object类了。泛型类可以根据需要,使用特定类型类了。泛型类可以根据需要,使用特定类型替换泛型类型,从而保证了类型安全性。当某个类型不支替换泛型类型,从而保证了类型安全性。当某个类型不支持泛型类时,编译器就会生成错误代码。持泛型类时,编译器就会生成错误代码。章节内容章节内容10.1 泛型概述泛型概述10.2 泛型类泛型类10.3
3、 泛型方法泛型方法10.4 泛型类的特性泛型类的特性10.5 小结小结10.1.1 泛型的概念泛型是对泛型是对CLR类型系统的一种扩展,用于定义某些细节类型系统的一种扩展,用于定义某些细节未指定的类型,通过参数化类型来实现在同一段代码上未指定的类型,通过参数化类型来实现在同一段代码上操作多种数据类型。操作多种数据类型。10.1.2 泛型的优点1性能性能2类型安全性类型安全性3二进制代码的重用二进制代码的重用4代码的扩展代码的扩展10.1.3 泛型的参数命名准则 务必使用具有描述性的名称为泛型类型参数命名,除非单个字母务必使用具有描述性的名称为泛型类型参数命名,除非单个字母的名称即可表示其含义,
4、而描述性名称不会包含更多含义。的名称即可表示其含义,而描述性名称不会包含更多含义。public interface ISessionChannel /*.*/public delegate TOutput Converter(TInput from);public class List /*.*/可以考虑以可以考虑以T作为具有单个字母类型参数的类型的类型参数名,作为具有单个字母类型参数的类型的类型参数名,例如下列代码。例如下列代码。public int IComparer()return 0;public delegate bool Predicate(T item);/定义了一个泛型委托定义
5、了一个泛型委托public struct Nullable where T:struct /*.*/使用泛型约束,使用泛型约束,参见参见10.4.2节节 通常将通常将“T”作为描述性类型参数名的前缀,代码如下。作为描述性类型参数名的前缀,代码如下。public interface ISessionChannel TSession Session get;10.2.1 泛型类的定义与实例化 定义泛型类的语法形式如下:定义泛型类的语法形式如下:访问修饰符访问修饰符 class类名类名 类体类体 其中,尖括号中是类型参数列表,类型参数列表可以由多个类型参数构成,其中,尖括号中是类型参数列表,类型参数
6、列表可以由多个类型参数构成,多个类型参数之间用逗号隔开。类型只是占位符,一般使用大写的多个类型参数之间用逗号隔开。类型只是占位符,一般使用大写的“T”、U”、“V”等作为类型参数。等作为类型参数。例如:例如:class Node private T data;public Node next;/省略其他代码省略其他代码10.2.1 泛型类的定义与实例化 在实例化泛型类时,需要使用具体的类型(如在实例化泛型类时,需要使用具体的类型(如int、double、string或者一个自定义类等)来代替类型参数,这样就可以在或者一个自定义类等)来代替类型参数,这样就可以在实例化泛型类时指定不同类型,生成具
7、有不同类型的实例。实例化泛型类时指定不同类型,生成具有不同类型的实例。泛型类的实例化语法形式如下:泛型类的实例化语法形式如下:类名类名 实例名实例名=new 类名类名(构造构造函数的实参函数的实参);对于上面定义的泛型类对于上面定义的泛型类Node,实例化时的代码如下。,实例化时的代码如下。class program public static void Main()Node MyNode=new Node();/省略其他代码省略其他代码 10.2.2 常用的泛型集合类泛型集合类非泛型集合类泛型举例ListArrayListList dinosaurs=new List();Dictionar
8、yHashtableDictionaryopenWith=new Dictionary();openWith.Add(txt,notepad.exe);QueueQueueQueue numbers=new Queue();number.Enqueue(one);StackStackStack numbers=new Stack();numbers.Push(one);numbers.Pop();SortedListSortdListSortedListopenWith=new SortedLst();openWith.Add(txt,notepad.exe);openWith.TryGetV
9、alue(tif,out value);10.3 泛型方法泛型方法是使用类型参数声明的方法。定义泛型方法的泛型方法是使用类型参数声明的方法。定义泛型方法的语法形式如下。语法形式如下。static void Swap(ref T lhs,ref T rhs)T temp;temp=lhs;lhs=rhs;rhs=temp;10.3 泛型方法下列代码演示一种使用下列代码演示一种使用int作为类型参数的方法调用方作为类型参数的方法调用方式。式。static void TestSwap()int a=1;int b=2;Swap(ref a,ref b);System.Console.WriteLin
10、e(a+b);10.4.1 默认关键字 在泛型类和泛型方法中会产生一个问题,在如下情况未知时,如何将默认值分配给参数化类型T?T是引用类型还是值类型。如果T为值类型,则它是数值还是结构。给定参数类型T的一个变量t,只有当T为引用类型时,语句t=null才会有效;只有当T为数值类型而不是结构时,语句t=0才会有效。解决方案为,使用default关键字。【例例10-4】default关键字演示关键字演示public class GenericList public T GetNext()T temp=default(T);/使用使用default关键字来给关键字来给temp初始化初始化 Node
11、current=head;if(current!=null)temp=current.Data;current=current.Next;return temp;10.4.1 默认关键字 在定义泛型类时,可以对客户端代码实例化类时在定义泛型类时,可以对客户端代码实例化类时用于类型参数的类型种类施加限制。如果客户端用于类型参数的类型种类施加限制。如果客户端代码尝试使用某个约束所不允许的类型来实例化代码尝试使用某个约束所不允许的类型来实例化类,则会导致编译错误。这些限制称为约束。约类,则会导致编译错误。这些限制称为约束。约束是使用上下文关键字束是使用上下文关键字where指定的,指定的,6种类型的
12、种类型的约束如下表所示。约束如下表所示。10.4.2 类型参数约束10.4.2 类型参数约束约束说明where T:struct使用结构约束,类型T必须是值类型where T:class类约束指定,类型T必须是引用类型,用于任何类、接口、委托或数组类型where T:基类名如where T:Foo类型参数必须是指定的基类或派生自指定的基类,where T:Foo表示为T指定的类必须是Foo类或这个类的派生类where T:接口名称如where T:IFoo类型参数必须是指定的接口或实现指定的接口,可以指定多个接口约束,where T:IFoo指定类型T必须实现接口IFoowhere T:new
13、()是一个构造函数约束,指定类型T必须有一个无参数的公共构造函数,与其他约束一起使用时,new()约束必须最后指定where T:U为T提供的类型参数必须是为U提供的参数,或派生自为U提供的参数,该约束也称为裸类型约束10.4.2 类型参数约束注意,在注意,在CLR 2.0中,只能为默认构造函数定义约束,中,只能为默认构造函数定义约束,不能为其他构造函数定义约束。不能为其他构造函数定义约束。之所以要使用类型参数约束是因为,如果要检查泛型列之所以要使用类型参数约束是因为,如果要检查泛型列表中的某个项以确定它是否有效,或者将它与其他某个表中的某个项以确定它是否有效,或者将它与其他某个项进行比较,则
14、编译器必须在一定程度上保证它需要调项进行比较,则编译器必须在一定程度上保证它需要调用的运算符或方法能够受到客户端代码可能指定的任何用的运算符或方法能够受到客户端代码可能指定的任何类型参数的支持。这种保证是通过对泛型类定义应用一类型参数的支持。这种保证是通过对泛型类定义应用一个或多个约束获得的。例如,基类约束告知编译器,仅个或多个约束获得的。例如,基类约束告知编译器,仅此类型的对象或由此类型派生的对象才可用作类型参数。此类型的对象或由此类型派生的对象才可用作类型参数。一旦编译器拥有该保证,它就能够允许在泛型类中调用一旦编译器拥有该保证,它就能够允许在泛型类中调用该类型的方法。该类型的方法。10.
15、4.2 类型参数约束可以对多个参数应用约束,并对一个参数应用多个约束,可以对多个参数应用约束,并对一个参数应用多个约束,例如下列代码。例如下列代码。public class Myclass where T:IFoo,new()10.4.2 类型参数约束【例例10-5】值类型约束演示值类型约束演示struct Point private int x;private int y;public Point(int a,int b)x=a;y=b;10.4.2 类型参数约束class Circle where T:struct private T p;public Circle()p=new T();
16、class Program static void Main(string args)Circle p=new Circle();通过约束类型参数,对于约束类型及其继承层次结通过约束类型参数,对于约束类型及其继承层次结构中的所有类型,可以增加所支持的操作和方法调构中的所有类型,可以增加所支持的操作和方法调用的数量。因此在设计泛型类或方法时,如果需要用的数量。因此在设计泛型类或方法时,如果需要对泛型成员执行任意操作(除简单赋值之外),或对泛型成员执行任意操作(除简单赋值之外),或调用调用System.Object不支持的任意方法,需要对该不支持的任意方法,需要对该类型参数应用约束。类型参数应用约
17、束。在应用在应用where T:class约束时,应避免对类型参数约束时,应避免对类型参数使用运算符使用运算符“=”和和“!=”,因为这些运算符仅能,因为这些运算符仅能够测试引用同一性而无法测试值相等性。即使在用够测试引用同一性而无法测试值相等性。即使在用作参数的类型中重载这些运算符时,也是如此。作参数的类型中重载这些运算符时,也是如此。10.4.2 类型参数约束10.4.3 继承 泛型类型可以实现泛型接口,也可以派生于一个类。泛型类型可以实现泛型接口,也可以派生于一个类。范型类型可以实现范型接口,代码如下。范型类型可以实现范型接口,代码如下。public class LinkedList:I
18、Enumberable /泛型类可以派生于泛型基类,代码如下。泛型类可以派生于泛型基类,代码如下。public class Base /public class Derived:Base /10.4.4 静态成员对于泛型类的静态成员需要特别注意。下面通过示例代码进行对于泛型类的静态成员需要特别注意。下面通过示例代码进行说明,说明,StaticDemo 类包含静态整形字段类包含静态整形字段x,代码如下。,代码如下。public class StaticDemo public static int x;由于对一个由于对一个string类型和一个类型和一个int类型使用了类型使用了StaticDem
19、o类,类,所以保存在两组静态字段中,代码如下。所以保存在两组静态字段中,代码如下。StaticDemo.x=4;StaticDemo.x=5;Console.WriteLine(StaticDemo.x);/输出输出4Console.WriteLine(StaticDemo.x);/输出输出510.5 小结本章主要介绍了本章主要介绍了CLR 2.0中的一个非常重要的特中的一个非常重要的特性性泛型。通过泛型可以创建独立于类型的类、泛型。通过泛型可以创建独立于类型的类、方法、结构和委托等。通过泛型可以解决方法、结构和委托等。通过泛型可以解决.NET 1.0和和.NET 1.1中困扰开发人员的编译器
20、错位和代码冗中困扰开发人员的编译器错位和代码冗余等问题,避免了对于数据类型的频繁强制转换和余等问题,避免了对于数据类型的频繁强制转换和装箱、拆箱,实现了代码重用、类型安全和高性能。装箱、拆箱,实现了代码重用、类型安全和高性能。泛型类最常用于集合,如链接列表、哈希表、堆栈、泛型类最常用于集合,如链接列表、哈希表、堆栈、队列和树等。诸如从集合中添加或移除项之类的操队列和树等。诸如从集合中添加或移除项之类的操作都能够以大致相同的方式执行,与所存储数据的作都能够以大致相同的方式执行,与所存储数据的类型无关。对于大多数需要集合类的方案,建议使类型无关。对于大多数需要集合类的方案,建议使用用.NET Framework类库中所提供的类。类库中所提供的类。10.5 小结泛型程序设计是一种区别于面向对象程序设计的设泛型程序设计是一种区别于面向对象程序设计的设计技术,巧妙地结合这两种设计方法,可以有效解计技术,巧妙地结合这两种设计方法,可以有效解决开发问题。决开发问题。