这几天认真研究了C#的委托与事件,下面对研究的知识总结一下,N年后找出来看一下应该也不错。
历史上,Windows API经常使用C语言风格的函数指针来创建称为回调函数或简称为回调的实体。使用回调,码农可以使一个函数返回报告给(即回调)程序中的另一个函数。
在.NET Framework里,回调仍是可能的,它们的功能是由使用更为安全和面向对象的委托(delegate)来完成的。本质上来讲,委托是一个类型安全的对象,它指向程序中另一个以后会被调用的方法(或多个方法)。委托类型包含3个得要的信息:
当一个委托被创建并提供了上述信息后,它可以在运行时动态调用其指向的方法。可以看到,.NET Framwwork中每个委托(包括自定义委托)都被自动赋予同步或异步访问方法的能力,可以不用手工创建与管理一个Thread对象而直接调用另一个辅助执行线程上的方法,这大大简化了编程工作。
使用C#定义委托:假定我们要创建一个名为BinaryOp的委托,它可以指向任何输入两个整数返回一个整数的方法:
public delegate int BinaryOp(int x,int y);
当C#编译器处理委托类型时,它先自动产生一个派生自System.MulticastDelegaet的密封类。这个类与它的基类System.Degelate 一起为委托提供必要的基础设施,以维护以后将要调用方法的列表。
写着写着突然没电,没保存,比改需求还痛苦,没办法,只能重来。
如果我们通过ildasm.exe来查看BinaryOp委托可以看到,生成的BinaryOp类定义了三个公共方法。Invoke()可能是核心方法,因为它被用来以同步方式调用委托类型维护的每个方法。BeginInvoke()和EndInvoke()方法能在第二个执行线程上异步调用当前方法。
编译器是如何确切知道怎样定义Invoke()、BeginInvoke()和EndInvoke()方法的呢?欲知后事如何,请看下面代码:
sealed class BinaryOp:System.MulticastDelegate { public BinaryOp(object target,uint functionAddress); public int Invoke(int x,int y); public IAsyncResult BeginInvoke(int x,int y,AsyncCallback cb,object state); public int EndInvoke(IAsyncResult result); }
初次接触委托可能会感觉很难。不要紧,接下来,让我们看一个非常简单的使用BinaryOp委托的示例,我们以前见过,这是一个名为Simple Delegate的控制台应用程序项目。
namespace SimpleDelegate { // 委托其实就相当于一个类型。这里,类型的名字叫BinaryOp public delegate int BinaryOp(int x,int y); class SimpleMath { public static int Add(int x, int y) { return x + y; } public static int Substract(int x, int y) { return x - y; } } class Program { static void Main(string[] args) { // 创建一个BinaryOp类型的实例,用静态Add方法初始化 BinaryOp d = new BinaryOp(Add); Console.WriteLine(d(10, 10)); Console.ReadLine(); } } }
.net委托是类型安全的。所以如果读者试图将一个不匹配模式的方法传入委托,将会收到编译期错误。例如,假定SimpleMath类定义了一个名为SquareNumber()的方法:
public class SimpleMath{ ... public static int SquareNumber(int a) { return a*a; } }
由于BinaryOp委托公可指向带有两个整数参数并返回一个整数的方法下面这段代码是非法的,将无法编译:
//错误!方法不匹配委托的模式 BinaryOp b2=new BinaryOp(SimpleMath.SquareNumber);
委托对象
我们在program类型中创建一个名为DisplayDegelateInfo()的静态方法丰富当前的示例。这个方法将输出传入的委托类型所维护的方法的名称和定义该方法的类的名称。通过迭代由GetInvocationList()返回的System.Delegate数组,调用每个对象的Target和method属性:
static void DisplayDelegateInfo(Delegate delObj) { foreach(Delegate d in delObj.GetInvocationList()) { Console.WriteLine("Method Name:{0}",d.Method); Console.WriteLine("Type Name:{0}",d.Target); } }