Windows API经常使用C语言风格的函数指针来创建称为回调函数或简称为回调的实体。使用回调,windows开发者可以处理按钮单击、鼠标移动、菜单选择以及内存中两个实体间的双向通信。
在.NET Framework里,回调功能则由更为安全和面向对象的委托(delegate)来完成。本质上来讲,委托是一个类型安全的对象,它指向程序中另一个以后会被调用的方法(或多个方法)。
委托有许多用途,但它们的大多数用于事件处理和线程。
1.定义委托类型
在C#中创建一个委托类型时,使用delegate关键字,委托的名称可以自由选择。例如:
public delegate intBinaryOp(int x, int y);
当C#编译器处理委托类型时,它先自动产生一个派生自System.MulticasrDelegate的密封类。这个类与它的基类System.Delegate一起为委托提供必要的基础设施,以维护以后将要调用方法的列表。如下图所示,我们通过菜单栏“工具”-“ILDasm”工具来查看BinaryOp委托。可以看到,为生成的BinaryOp类定义了三个公共方法:Invoke(),BeginInvoke()和EndInvoke()。
Invoke()是核心方法,因为它用来以同步方式调用委托对象维护的方法。这里的同步就是指调用者必须等待方法调用完毕后才能继续执行。但在C#中,我们不直接调用Invoke()方法,而是使用适当地C#语法使得Invoke()在后台调用(下面会举例说明)。
BeginInvoke()和EndInvoke()方法能在第二个执行线程上异步调用当前的方法。尽管.NET基础类库为多线程专门提供了整个命名空间(System.Threading),而委托顺带就提供了这一功能。
请注意,由Invoke()方法定义的参数和返回值完全匹配BinaryOp委托类型。BeginInvoke()成员前面的参数也基于BianryOp委托,但BeginInvoke()方法将总是提供最后两个参数(AsyncCallback类型和object类型),而且返回IAsyncResult接口,用于异步方法调用。最后,EndInvoke()方法的返回值类型与定义的委托返回类型相同,且总是以一个实现了IAsyncResult接口的对象作为唯一的参数。
2. 最简单的委托示例
如下面示例所示,我们定义了一个参数和返回类型都为double的ProcessDelegate委托,另外又定义了两个与委托有相同返回类型和参数列表的函数Multiple和Divide。在Main函数中使用新的委托类型声明了一个委托变量process,后将Multiple函数引用赋给process委托变量。注,必须使用new关键字创建一个委托。
尽管一般在C#应用程序中不显示调用Invoke()方法,但是我们也完全可这么做,例如“Console.WriteLine("Result is {0}", process(10, 2)); ”,我们也可以写成“Console.WriteLine("Result is {0}", process.Invoke(10, 2)); ”
<span style="font-size:14px;">using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Delegate
{
class Program
{
//定义一个委托
delegate double ProcessDelegate(double param1, double param2);
//Multiply函数
static double Multiply(double param1, double param2)
{
return param1 * param2;
}
//Divide函数
static double Divide(double param1, double param2)
{
return param1 / param2;
}
static void Main(string[] args)
{
//声明一个ProcessDelegate委托类型的变量
ProcessDelegate process;
//初始化委托为Multiply函数的引用
process = new ProcessDelegate(Multiply);
//通过委托调用委托所引用的函数
Console.WriteLine("Result is {0}", process(10, 2));
//改变委托的为Divide函数的引用
process = new ProcessDelegate(Divide);
Console.WriteLine("Result is {0}", process(10, 2));
Console.ReadKey();
}
}
}</span>
其运行结果为: