《六、、understand_generics》由会员分享,可在线阅读,更多相关《六、、understand_generics(14页珍藏版)》请在金锄头文库上搜索。
1、 1 / 14 C#中的泛型1 / 14第第 1 章章 C#中的泛型中的泛型.Net 1.1 版本最受诟病的一个缺陷就是没有提供对泛型的支持。通过使用泛型,我们可以极大地提高代码的重用度,同时还可以获得强类型的支持,避免了隐式的装箱、拆箱,在一定程度上提升了应用程序的性能。本文将系统地为大家讨论泛型,我们先从理解泛型开始。1.1 理解泛型理解泛型1.1.1 为什么要有泛型?为什么要有泛型?我想不论大家通过什么方式进入了计算机程序设计这个行业,都免不了要面对数据结构和数据结构和算法算法这个话题。因为它是计算机科学的一门基础学科,往往越是底层的部分,对于数据结构或者算法的时间效率和空间效率的要求就
2、越高。比如说,当你在一个集合类型(例如 ArrayList)的实例上调用 Sort()方法对它进行排序时,.Net 框架在底层就应用了快速排序快速排序算法。.Net 框架中快速排序方法名称叫 QuickSort(),它位于 Array 类型中,这可以通过 Reflector.exe1工具查看到。我们现在并不是要讨论这个 QuickSort()实现的好不好,效率高还是不高,这偏离了我们的主题。但是我想请大家思考一个问题:如果由你来实现一个排序算法,你会怎么做?好吧,我们把题目限定得再窄一些,我们来实现一个最简单的冒泡排序(Bubble Sort)算法,如果你没有使用泛型的经验,我猜测你可能会毫不
3、犹豫地写出下面的代码来,因为这是大学教程的标准实现:public class SortHelperpublic void BubbleSort(int array) int length = array.Length;for (int i = 0; i = 1; j-) 1 你可以将它理解为查看.Net 框架源码的一个工具。2 / 14 C#中的泛型2 / 14/ 对两个元素进行交换if (arrayj = 1; j-) / 对两个元素进行交换if (arrayj = 1; j-) / 对两个元素进行交换if (arrayj = 1; j-) / 对两个元素进行交换if (arrayj pub
4、lic void BubbleSort(T array)/ 方法实现体我们在类名称的后面加了一个尖括号,使用这个尖括号来传递我们的占位符,也就是类型参数。接下来,我们来看看如何来使用它,当我们需要为一个 int 类型的数组排序时:SortHelper sorter = new SortHelper();int array = 8, 1, 4, 7, 3 ;sorter.BubbleSort(array);当我们需要为一个 byte 类型的数组排序时:SortHelper sorter = new SortHelper();byte array = 8, 1, 4, 7, 3 ;sorter.B
5、ubbleSort(array);相信你已经发觉,其实上面所做的一切实现了一个泛型类。这是泛型的一个最典型的应用,可以看到,通过使用泛型,我们极大地减少了重复代码,使我们的程序更加清爽,泛型类就类似于一个模板,可以在需要时为这个模板传入任何我们需要的类型。我们现在更专业一些,为这一节的占位符起一个正式的名称,在.Net 中,它叫做类型参数类型参数 (Type Parameter) ,下面一小节,我们将学习类型参数约束。7 / 14 C#中的泛型7 / 141.1.2 类型参数约束类型参数约束实际上,如果你运行一下上面的代码就会发现它连编译都通过不了,为什么呢?考虑这样一个问题,假如我们自定义一
6、个类型,它定义了书,名字叫做 Book,它含有两个字段:一个是int 类型的 Id,是书的标识符;一个是 string 类型的 Title,代表书的标题。因为我们这里是一个范例,为了既能说明问题又不偏离主题,所以这个 Book 类型只含有这两个字段:public class Book private int id;private string title;public Book() public Book(int id, string title) this.id = id;this.title = title;public int Id get return id; set id = val
7、ue; public string Title get return title; set title = value; 现在,我们创建一个 Book 类型的数组,然后试着使用上一小节定义的泛型类来对它进行排序,我想代码应该是这样子的:Book bookArray = new Book2;8 / 14 C#中的泛型8 / 14Book book1 = new Book(124, “.Net 之美“);Book book2 = new Book(45, “C# 3.0 揭秘“);bookArray0 = book1;bookArray1 = book2;SortHelper sorter = n
8、ew SortHelper();sorter.BubbleSort(bookArray);foreach (Book b in bookArray) Console.WriteLine(“Id:0“, b.Id);Console.WriteLine(“Title:0n“, b.Title);可能现在你还是没有看到会有什么问题,你觉得上一节的代码很通用,那么让我们看得再仔细一点,再看一看 SortHelper 类的 BubbleSort()方法的实现吧,为了避免你回头再去翻上一节的代码,我将它复制了下来:public void BubbleSort(T array) int length = a
9、rray.Length;for (int i = 0; i = 1; j-) / 对两个元素进行交换if (arrayj 泛型类并不知道,怎么办呢?我们需要告诉 SortHelper类(准确说是告诉编译器) ,它所接受的 T 类型参数必须能够进行比较,换言之,就是实现 IComparable 接口,这便是本小节的主题:泛型约束。为了要求类型参数 T 必须实现 IComparable 接口,我们像下面这样重新定义SortHelper:public class SortHelper where T:IComparable / CODE:实现略上面的定义说明了类型参数 T 必须实现 IComapra
10、ble 接口,否则将无法通过编译,从而保证了方法体可以正确地运行。因为现在 T 已经实现了 IComparable,而数组 array 中的成员是 T的实例,所以当你在 arrayi后面点击小数点“.”时,VS200 智能提示将会给出 IComparable 的11 / 14 C#中的泛 型11 / 14成员,也就是 CompareTo()方法。我们修改 BubbleSort()类,让它使用 CompareTo()方法来进行比较:public class SortHelper where T:IComparablepublic void BubbleSort(T array) int leng
11、th = array.Length;for (int i = 0; i = 1; j-) / 对两个元素进行交换if (arrayj.CompareTo(arrayj - 1) where T:IComparable / CODE:略13 / 14 C#中的泛 型13 / 14public void SpeedSort(T array) / CODE:实现略这里穿插讲述一个关于类型设计的问题:确切的说,将 SpeedSort()方法放在 SuperCaculator中是不合适的。为什么呢?因为它们的职责混淆了,SuperCaculator 的意思是“超级计算器” ,那么它所包含的公开方法都应该
12、是与计算相关的,而 SpeedSort()出现在这里显得不伦不类,当我们发现一个方法的名称与类的名称关系不大时,就应该考虑将这个方法抽象出去,把它放置到一个新的类中,哪怕这个类只有它一个方法。这里只是一个演示,我们知道存在这个问题就可以了。好了,我们回到正题,尽管现在SuperCalculator 类确实可以完成我们需要的工作,但是它的使用却变得复杂了,为什么呢?因为 SpeedSort()方法污染了它,仅仅为了能够使用 SpeedSort()这一个方法,我们却不得不将类型参数 T 加到 SuperCalculator 类上,使得即使不调用 SpeedSort()方法时,创建 SuperCal
13、culator 实例时也得接受一个类型参数。为了解决这个问题,我们自然而然地会想到:有没有办法把类型参数 T 加到方法上,而非整个类上,也就是降低 T 作用的范围。答案是可以的,这便是本小节的主题:泛型方法。类似地,我们只要修改一下 SpeedSort()方法的签名就可以了,让它接受一个类型参数,此时SuperCalculator 的定义如下:public class SuperCalculator/ CODE:其他实现略public void SpeedSort(T array) where T : IComparable / CODE:实现略接下来我们编写一段代码来对它进行一个测试:Boo
14、k bookArray = new Book2;Book book1 = new Book(124, “C# 3.0 揭秘“);14 / 14 C#中的泛 型14 / 14Book book2 = new Book(45, “.Net 之美“);SuperCalculator calculator = new SuperCalculator();calculator.SpeedSort(bookArray);因为 SpeedSort()方法并没有实现,所以这段代码没有任何输出,如果你想看到输出,可以简单地把上面冒泡排序的代码贴进去,这里我就不再演示了。这里我想说的是一个有趣的编译器能力,它可以推断出你传递的数组类型以及它是否满足了泛型约束,所以,上面的SpeedSort()方法也可以像下面这样调用:calculator.SpeedSort(bookArray);这样尽管它是一个泛型方法,但是在使用上与普通方法已经没有了任何区别。1.1.4 总结总结本节中我们学习了掌握泛型所需要的最基本知识,你看到了需要泛型的原因,它可以避免重复代码,还学习到了如何使用类型参数约束和泛型方法。拥有了本节的知识,你足以应付日常开发中的大部分场景。在下面两节,我们将继续泛型的学习,其中包括泛型在集合类中的应用,以及泛型的高级话题。