if (obj1 == obj2) …
象==和!=这样的运算符的实现对于值类型和引用类型都是不同的。如果随意地允许之,代码的行为可能很出乎你的意料。另外一种限制是缺省构造器的使用。例如,如果你编码象new T(),会出现一个编译错,因为并非所有的类都有一个无参数的构造器。如果你真正编码象new T()来创建一个对象,或者使用象==和!=这样的运算符,情况会是怎样呢?你可以这样做,但首先要限制可被用于参数化类型的类型。读者可以自己先考虑如何实现之。
8. 约束机制及其优点
一个泛型类允许你写自己的类而不必拘泥于任何类型,但允许你的类的使用者以后可以指定要使用的具体类型。通过对可能会用于参数化的类型的类型施加约束,这给你的编程带来很大的灵活性--你可以控制建立你自己的类。让我们分析一个例子:
例5.需要约束:代码不会编译成功
public static T Max<T>(T op1, T op2)
{
if (op1.CompareTo(op2) < 0)
return op1;
return op2;
}
例5中的代码将产生一个编译错误:
Error 1 ’T’ does not contain a definition for ’CompareTo’
假定我需要这种类型以支持CompareTo()方法的实现。我能够通过加以约束--为参数化类型指定的类型必须要实现IComparable接口--来指定这一点。例6中的代码就是这样:
例6.指定一个约束
public static T Max<T>(T op1, T op2) where T : IComparable
{
if (op1.CompareTo(op2) < 0)
return op1;
return op2;
}
在例6中,我指定的约束是,用于参数化类型的类型必须继承自(实现)Icomparable。下面的约束是可以使用的:
where T : struct 类型必须是一种值类型(struct)
where T : class 类型必须是一种引用类型(class)
where T : new() 类型必须有一个无参数的构造器
where T : class_name 类型可以是class_name或者是它的一个子类
where T : interface_name 类型必须实现指定的接口
你可以指定约束的组合,就象: where T : IComparable, new()。这就是说,用于参数化类型的类型必须实现Icomparable接口并且必须有一个无参构造器。
9. 继承与泛型
一个使用参数化类型的泛型类,象MyClass1<T>,称作开放结构的泛型。一个不使用参数化类型的泛型类,象MyClass1<int>,称作封闭结构的泛型。
你可以从一个封闭结构的泛型进行派生;也就是说,你可以从另外一个称为MyClass1的类派生一个称为MyClass2的类,就象:
public class MyClass2<T> : MyClass1<int>
你也可以从一个开放结构的泛型进行派生,如果类型被参数化的话,如:
public class MyClass2<T> : MyClass2<T>
是有效的,但是
public class MyClass2<T> : MyClass2<Y>
是无效的,这里Y是一个被参数化的类型。非泛型类可以从一个封闭结构的泛型类进行派生,但是不能从一个开放结构的泛型类派生。即:
public class MyClass : MyClass1<int>
是有效的, 但是
public class MyClass : MyClass1<T>
是无效的。
10. 泛型和可代替性
当我们使用泛型时,要小心可代替性的情况。如果B继承自A,那么在使用对象A的地方,可能都会用到对象B。假定我们有一篮子水果(a Basket of Fruits (Basket<Fruit>)),而且有继承自Fruit的Apple和Banana(皆为Fruit的种类)。一篮子苹果--Basket of Apples (Basket<apple>)可以继承自Basket of Fruits (Basket<Fruit>)?答案是否定的,如果我们考虑一下可代替性的话。为什么?请考虑一个a Basket of Fruits可以工作的方法:
public void Package(Basket<Fruit> aBasket)
{