C#中的委托(Delegates in C#)- part two

前文讨论了什么是C#中的委托,那么,C#为什么要引入委托呢?

 

让我们把话题扯远一点,先来看一个著名的排序算法。

 

ACM图灵奖获得者、微软剑桥研究院的首席科学家 C. A. R. Hoare1960年作为前苏联莫斯科国立大学的访问学生,在从事一个机器翻译的项目时,为了对要翻译的词进行排序,开发了一个高效排序算法,这就是大名鼎鼎的快速排序算法——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#中是如何实现的呢?

 

下面是qsortC#版本:

 

public static void Sort<T>(T[] Array, Comparison<T> comparison);

 

对比CqosrtC#Sort,基本形式是一样的,只不过void *basesize_t numsize_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)。

 

posted @ 2012-03-15 23:17  野峰-WildPeak  阅读(274)  评论(0编辑  收藏  举报