回调(callback)函数是Windows编程的一个重要部分。如果您具备C或C++编程背景,就曾在许多Windows API中使用过回调。VB添加了AddressOf关键字后,开发人与就可以利用以前一度收到限制的API了。回调函数实际上是方法调用的指针,也称为函数指针,是一个非常强大的编程特性。.NET以委托的形式实现了函数指针的概念。它们的特殊之处是,与C函数指针不同,.NET委托是类型安全的。这说明,C中的函数指针只不过是一个指向存储单元的指针,我们无法说出这个指针实际指向什么,像参数和返回类型等就更无从知晓了。如本章所述,.NET委托作为一种类型安全的操作。下面我们将学习.NET如何将委托用作实现事件的方式。
一、委托
当要把方法传送给其他方法时,需要使用委托。要了解他们的含义,可以看看下面的代码:
int i = int.Parse("99");
我们习惯于吧数据作为参数传递给方法,如上面的例子所示。所以,给方法传送另一个方法听起来有点奇怪。而有时某个方法执行的操作并不是针对数据进行的,而是要对另一个方法进行操作,这就比较复杂了。在编译时我们不知道第二个方法是什么,这个信息只能在运行时得到,所以需要把第二个方法作为参数传递给第一个方法,这听起来很令人迷惑,下面几个实例来说明:
1、启动线程-----在C#中,可以告诉计算机并行运行某些新的执行序列。这种序列就称为线程,在积累System.Threading.Thread的一个实例上使用方法Start(),就可以开始执行一个线程。如果要告诉计算机开始一个新的执行序列,就必须说明要在哪里执行该序列。必须为计算机提供开始执行的方法的细节,即Thread.Start()方法必须带有一个参数,该参数定义了要由线程调用的方法。
2、通用库类------有许多库包含执行各种标准任务的代码。这些库通常可以自我包含。这样在编写库时,就会知掉任务如何执行。但是有时在任务中还包含子任务。只有使用该库的客户机代码才知道如何执行这些子任务。例如编写一个类,它带有一个对象数组,并把它们按升序排列。但是,排序的部分过程会涉及到重复使用数组中的两个对象,比较他们,看看哪一个应放在前面。如果要编写的类必须能给任何对象数组排序,就无法提前告诉计算机应如何比较对象。处理类中对象数组的客户机代码也必须告诉类如何比较要排序的对象。换言之,客户机代码必须给类传递某个可以进行这种比较合适的细节。
3、事件------一般是通知代码发生了什么事件。GUI编程主要是处理事件。在发生事件时,运行库需要知道应执行哪个方法。这就需要把处理时间的方法传送为委托的一个参数。这些将在后面讨论。
前面建立了有时把方法名作为参数传递给其他方法的规则。下面需要指出如何完成这一过程。最简单的方式是把方法名作为参数传递出去。例如在前面的线程示例中,假定要启动一个新线程,且有一个叫作EntryPoint()的方法,该方法是开始运行线程时的地方。
void EntryPoint()
{
//do whatever the new thread needs to do
}
也可以用下面的代码开始执行新线程:
Thread NewThread = new Thread();
Thread.Start(EntryPoint); //WRONG
实际上,这是一种很简单的方式,在一些语言如C和C++中使用的就是这种方式(在C和C++中,参数EntryPoint是一个函数指针)。
但这种直接的方法会导致一些问题,例如类型的安全性,在进行面向对象编程时,方法很少是孤立存在的,在调用前,通常需要与类实例相关联。而这种方法并没有考虑到这个问题。所以.NET Framework在语法上不允许使用这种直接的方法。如果要传递方法,就必须把方法的细节封装在一种新类型的对象中,即委托。委托只是一种特殊的对象类型,其特殊之处在于,我们以前定义的所有的对象都包含数据,而委托包含的只是方法的细节。