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