前文讨论了什么是C#中的委托,那么,C#为什么要引入委托呢?
让我们把话题扯远一点,先来看一个著名的排序算法。
ACM图灵奖获得者、微软剑桥研究院的首席科学家 C. A. R. Hoare,1960年作为前苏联莫斯科国立大学的访问学生,在从事一个机器翻译的项目时,为了对要翻译的词进行排序,开发了一个高效排序算法,这就是大名鼎鼎的快速排序算法——QuickSort。快速排序的平均时间复杂度是O(nlogn),最坏时间复杂度是O(n2),实际上QuickSort比其他的O(nlogn)算法往往要快一些。
算法原理
QuickSort是一个典型的“分而治之”的算法,在要排序的表(list or array)中选取一个值作为分界点(pivot),将这个表划分为两个子表(list1 和 list2),其中list1 中的值都小于等于分界点的值,而 list2 中的值都大于分界点的值,然后对 list1 和 list2 递归重复这种划分法,直到最后的子表中的元素为0或只有一个元素为止。
算法伪码
function quicksort('array')
if length('array') <= 1 then return 'array'
select and remove a pivot value 'pivot' from 'array'
create empty lists 'less' and 'greater'
for each 'x' in 'array'
if 'x' <= 'pivot' then append 'x' to 'less'
else append 'x' to 'greater'
return concatenate(quicksort('less'), 'pivot', quicksort('greater'))
从以上算法原理的描述及伪码实现中可以看出,QuickSort是一个基于比较的算法,list1(伪码中的less)和list2(伪码中的greater)中的元素是通过与分界点(伪码中的pivot)进行比较而得来的。
既然是基于比较的算法,这就要求QuickSort要能够对其进行排序的元素进行比较!然而,并非所有数据类型都具有“天然的(或自然的)”比较操作,尤其是用户自定义类型,当然我们可以通过C++或C#中的操作符重载来定义比较操作,但对于像C这类没有操作符重载功能的语言,如何实现对用户自定义类型的比较操作呢?
人们找到了一个有效的途径——把比较操作“委托”给用户(client)去实现!
void qsort(void *base, size_t num, size_t width, int (__cdecl *compare)(const void*, const void *));
这是 Microsoft CRT 的QuickSort 的函数原型(prototype),其中:
base
要排序的表(数组)
num
表中的元素个数
width
每个元素所占的字节数(元素大小)
compare
(用户提供的)比较函数,其中第一个参数是分界点,第二个参数是要与分界点进行比较的元素
这样一来,qsort算法就一般化了,只要表中的元素具有同样的大小,而且提供了对元素进行比较操作的函数,就可以用qsort对表进行排序,而对于qsort而言,并不需要关心要比较的元素是什么数据类型。
一个C语言的qsort示例如下:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
Int compare(const void *arg1, const char* arg2)
{
return _stricmp(*(char **)arg1, *(char **)arg2);
}
int main(int argc, char **argv)
{
/* sort command-line args using QuickSort algorithm */
qsort((void *)argv, (size_t)argc, sizeof(char *), compare);
int i;
for (i = 0; i < argc; ++i)
{
printf("%s", argv[i]);
}
}
在这个例子中,利用compare函数实现了字符串的比较操作,从而可以用qsort对命令行参数进行排序。
像 compare 这样的函数,也称为回调函数(callback)。回调函数机制在现代软件开发中大量应用,尤其是GUI的事件处理。
上面这种将函数作为参数传递,其实现机制是指针,即实际传递的是函数的指针。
那,qsort在 C#中是如何实现的呢?
下面是qsort的C#版本:
public static void Sort<T>(T[] Array, Comparison<T> comparison);
对比C版qosrt和C#版Sort,基本形式是一样的,只不过void *base,size_t num,size_twidth三个参数由T[] Array一个参数表示了。这里同样有一个比较函数,这个比较函数的类型 Comparison<T> 实际上就是一个委托类型:
public delegate int Comparison<T>(T x, T y);
对比compare的声明:
int (*compare) (const void *arg1, const void *arg2);
可以看出,C版的compare是一个函数指针,而C#版的comparison是一个委托类型,而这个委托类型实际上就是比较函数comparison的一个包装(wrapper),这一点,前文已经讨论过了。
由于指针是一种非安全数据类型,C#不提倡使用。而在必须使用函数指针的场合,C#提供了委托类型,将函数指针作为类包装起来,这样编译器可以做静态检查,提高了程序的类型安全,所以说C#是一种类型安全的语言(type-safe programming language)。