20-泛型约束
16.5.2 泛型约束
上述例子中,如果保持原冒泡算法中的“if (array[j] < array[j - 1])”语句不变,或去掉“where T : IComparable”代码部分,程序将出现编译错误。这主要是因为泛型T是后来绑定的,因此在编译时编译器无法得知T的实例是采用什么样的标准来进行大小比较的。对于上例中的简单数据类型比较,我们只需给参数类型T添加“where T : IComparable”类型约束,但如何用泛型方法实现对复杂数据类型的比较呢?下面我们举例说明。
假如有一个自定义的类Book,它包含两个私有字段_id和_title,两个外部属性ID和Title,以及两个构造函数,代码如下。
01 public class Book
02 {
03 private int _id; //表示ID的字段
04 private String _title; //表示书名的字段
05 public Book() { } //无参数构造函数
06 public Book(int id,string title) {
07 this._id=id;
08 this._title=title; //有参数构造函数
09 }
10 public int ID {
11 get{return _id;}
12 set{_id=value;}
13 } //表示ID的属性
14 public string Title{ //表示书名的属性
15 get{return _ title;}
16 set{_ title=value;}
17 }
18 }
现在创建一个Book型的数组,然后用SortHelper类中的泛型方法对其进行排序。
01 public class Test
02 {
03 static void Main() {
04 Book[] bookArray=new Book[2];
05 Book book1=new Book(1,"guowenhui,");
06 Book book2=new Book(2,"ongyaguang,");
07 bookArray[0]=book1;
08 bookArray[1]=book2; //有参数构造函数
09 SortHelper<Book> sort=new SortHelper<Book>();
10 Sort.BubbleSort(bookArray);
11 foreach (Book b in bookArray) {
12 Console.WriteLine(“Id:{0}”, b.ID);
13 Console.WriteLine(“Title:{0}\n”, b.Title); //表示客户ID的属性
14 }
15 }
16 }
上面的代码将不能通过编译,这是因为我们现在要比较的是复杂类型对象Book,book1和book2到底谁大,这涉及一个判断依据的问题。如何来实现这种复杂类型对象的比较呢?答案是让需要进行比较的对象类实现IComparable接口。也就是说,只有实现了IComparable接口的类型才能作为类型参数被传入,即我们需要对传入参数的类型进行一些约束,这就是我们要讲的泛型约束。
接下来我们就让Book类来实现IComparable接口,即在类的内部定义一个比较的标准,我们这里采用的标准是比较ID,Book类中变化的代码如下。
01 public class Book: IComparable
02 {
03 public int CompareTo(object obj) //实现接口
04 {
05 Book book2=(Book)obj;
06 return this.ID.CompareTo(book2.ID);
07 }
08 …… //其他代码不变
09 }
再次运行测试代码,将正确输出书本ID的比较结果。
C#泛型要求对“所有泛型类型或泛型方法的类型参数”的任何假定,都要基于“显式的约束”,以维护C#所要求的类型安全。“显式约束”由where子句表达,可以指定“基类约束”、“接口约束”、“构造器约束”、“值类型/引用类型约束”四种约束(在本例中我们实现的是接口约束)。“显式约束”并非必需,如果没有指定“显式约束”,范型类型参数将只能访问System.Object类型中的公有方法。