事件和委托的用途在于实现Runtime的两种机制: 1)事件通知 2)事件处理
委托(delegate)是指向一个方法的指针,通过制定一个委托名称,来调用方法。可以动态的更改一个委托引用的方法。
(委托类型用来响应应用程序中的回调(callback), 委托和C++的函数指针相似,但是委托是类型安全的。)
委托类型包括3个重要的信息:
- 它所调用的方法的名称
- 该方法的参数(可选)
- 该方法的返回值(可选)
.NET委托既可以指向静态方法,也可以指向实例方法。并且可以在运行时动态调用其指向的方法。
.NET的每一个委托都被自动赋予了同步或异步访问方法的能力,可以不用创建而后管理Thread对象而直接调用辅助线程上的方法。
定义委托类型
//此委托可以指向任何传入两个整数,返回的一个整数的方法。
//为了便于理解,在原理上等同于C/C++, typedef int (*BinaryOperation)(int x, int y), 在这里 BinaryOperation 就是函数指针类型,此函数返回int值 public delegate int BinaryOperation(int x, int y);
当编译器处理委托类型时,先自动产生一个派生自System.MulticastDelegate的密封类,并定义了3个public方法
- Invoke()为核心方法,用来以同步方式调用委托对象维护的每个方法。
- BeginInvoke()和EndInvoke()在第二个执行线程上异步调用当前方法。
创建一个类SimpleMath,包含方法(加,减法)。完全匹配上述委托的调用机制(传入两个int参数,返回1个int参数).
public class SimpleMath { public static int Add(int x, int y) { return x + y; } public static int Subtract(int x, int y) { return x - y; } }
创建委托对象,调用方法。
// 创建一个 BinaryOperation 对象,并指向 SimpleMath.Add()静态方法。
// C/C++: BinaryOperation bo = SimpleMath.Add; int result = bo(111, 222);
BinaryOperation bo = new BinaryOperation(SimpleMath.Add); // 使用委托对象调用 SimpleMath.Add()方法。 Console.WriteLine("111 + 222 = {0}", bo(111,222));
在底层,运行库实际上在MulticastDelegate派生类上隐式调用了编译器生成的Invoke()方法。实际上也可以显式调用Invoke()方法。
// 显式调用 SimpleMath.Add()方法。 Console.WriteLine("111 + 222 = {0}", bo.Invoke(111,222));
.NET是类型安全的,如果试图将一个不匹配的方法(例如输入一个int参数)传入委托,将会收到编译器错误。
.NET支持多路广播,即一个委托对象可以维护一个可调用方法的列表。给一个委托对象添加多个方法时,不用直接分配,重载+=操作符即可。
bo += SimpleMath.Subtract;
从委托的调用列表中移除成员方法的时候的时候,用Remove()方法,或者用-=操作符。
所有代码如下:
namespace ConsoleApplication1 { class Program { public delegate int BinaryOperation(int x, int y); public class SimpleMath { public static int Add(int x, int y) { Console.WriteLine("Method Add is invoked."); return x + y; } public static int Subtract(int x, int y) { Console.WriteLine("Method Subtract is invoked."); return x - y; } } static void Main(string[] args) { // 创建一个 BinaryOperation 对象,并指向 SimpleMath.Add()方法。 BinaryOperation bo = new BinaryOperation(SimpleMath.Add); // 使用委托对象调用 SimpleMath.Add()方法。 Console.WriteLine("111 + 222 = {0}", bo(111,222)); // 增加方法列表 bo += SimpleMath.Subtract; Console.WriteLine("Final result is: {0}", bo(111, 222)); Console.ReadLine(); } } }
输出结果:
Method Add is invoked.
111 + 222 = 333
Method Add is invoked.
Method Subtract is invoked.
Final result is: -111
在C/C++ 中的函数指针更容易理解 delegate. 上面的例子等同如下:
head 文件,定一了一个函数指针,和一个类。
typedef int (*BinaryOperation)(int x, int y); class SimpleMath { public: static int Add(int x, int y); static int SubStract(int x, int y); };
cpp文件
int _tmain(int argc, _TCHAR* argv[]) { BinaryOperation bo = SimpleMath::Add; int result = bo(12, 23); printf("%d\n", result); return 0; } int SimpleMath::Add(int a, int b) { return a+b; } int SimpleMath::SubStract(int a, int b) { return a-b; }
=================================================
再举个例子理解一下“委托”。
委托就是请人办事。
譬如A项目组有项目经理,软件Lead,软件工程师,测试工程师等组成。软件组会做很多工作,通常都是软件Lead分配的。但是软件Lead本人是不亲自做事的。他是对外部的窗口。比如他手下有两个牛X的工程师,一个叫Bill,一个叫Steven。活儿都是他俩干的。
也就是说,所有的工作,都是软件Lead“委托”Bill, 和 Steven来做的。
static void Main(string[] args) { SoftwareLead fred = new SoftwareLead(); fred.doWork(); }
public class SoftwareLead { public delegate void AssignSoftwareWork(); private SoftwareEngineer bill = new SoftwareEngineer("Bill"); private SoftwareEngineer steven = new SoftwareEngineer("Steven"); public void doWork() { AssignSoftwareWork doWork = new AssignSoftwareWork(bill.UpdateFirmware); doWork(); } }
public class SoftwareEngineer { private string engineerName; public SoftwareEngineer(string name) { this.engineerName = name; } public void UpdateFirmware() { Console.WriteLine("{0} is updating firmware...", this.engineerName); } public void FixBugs() { Console.WriteLine("{0} is fixing bugs...", this.engineerName); } }
委托当然可以请“多人”办事。也就是说可以给委托增加更多的方法。/* 指针函数列表*/
比如这次的软件组的工作是让Bill升级FW,然后让Bill再fix一些bug,同时也会安排steven来fix一些bug。
public void doWork() { AssignSoftwareWork doWork = new AssignSoftwareWork(bill.UpdateFirmware); doWork += new AssignSoftwareWork(bill.FixBugs); doWork += new AssignSoftwareWork(steven.FixBugs); doWork(); }
输出:
Bill is updating firmware... Bill is fixing bugs... Steven is fixing bugs...
显然,上述的code还是有些问题的,Software Lead的code过于繁琐,并且,私自的把另外两个小弟做成了软件Lead类的字段。
再使用泛型委托Action 和 Lambda表达式后,如下代码更贴近现实生活。
SoftwareLead类 喊出一句话后,就assign工作。(更加通用了。)。
public class SoftwareLead { public void doWork(Action assignWork) { Console.WriteLine("Hey buddies, let's start working..."); assignWork(); } }
具体的工作呢,在SoftwareLead 的一个对象里才会说明。
static void Main(string[] args) { SoftwareLead fred = new SoftwareLead(); SoftwareEngineer bill = new SoftwareEngineer("Bill"); SoftwareEngineer steven = new SoftwareEngineer("Steven"); fred.doWork(() => { bill.UpdateFirmware(); steven.FixBugs(); }); }
参考:
http://www.cnblogs.com/JimmyZhang/archive/2007/09/23/903360.html