从前,在南方一块奇异的土地上,有个工人名叫彼得,他非常勤奋,对他的老板总是百依百顺。但是他的老板是个吝啬的人,从不信任别人,坚决要求随时知道彼得的工作进度,以防止他偷懒。但是彼得又不想让老板呆在他的办公室里站在背后盯着他,于是就对老板做出承诺:无论何时,只要我的工作取得了一点进展我都会及时让你知道。彼得通过周期性地使用“带类型的引用” (原文为:“ typed reference” 也就是 delegate?? )“回调”他的老板来实实现他的承诺,如下:
讲故事谈.NET委托:一个C#睡前的故事_NETclass Worker {    
讲故事谈.NET委托:一个C#睡前的故事_NETpublic void Advise(Boss boss) { _boss = boss; }    
讲故事谈.NET委托:一个C#睡前的故事_NETpublic void DoWork() {    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine(“ 工作 : 工作开始 ”);    
讲故事谈.NET委托:一个C#睡前的故事_NETif( _boss != null ) _boss.WorkStarted();    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine(“ 工作 : 工作进行中 ”);    
讲故事谈.NET委托:一个C#睡前的故事_NETif( _boss != null ) _boss.WorkProgressing();    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine("“ 工作 : 工作完成 ”");    
讲故事谈.NET委托:一个C#睡前的故事_NETif( _boss != null ) {    
讲故事谈.NET委托:一个C#睡前的故事_NETint grade = _boss.WorkCompleted();    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine(“ 工人的工作得分= ” + grade);    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NETprivate Boss _boss;    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NETclass Boss {    
讲故事谈.NET委托:一个C#睡前的故事_NETpublic void WorkStarted() { /* 老板不关心。 */ }    
讲故事谈.NET委托:一个C#睡前的故事_NETpublic void WorkProgressing() { /* 老板不关心。 */ }    
讲故事谈.NET委托:一个C#睡前的故事_NETpublic int WorkCompleted() {    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine(“ 时间差不多! ”);    
讲故事谈.NET委托:一个C#睡前的故事_NETreturn 2; /* 总分为 10 */    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NETclass Universe {    
讲故事谈.NET委托:一个C#睡前的故事_NETstatic void Main() {    
讲故事谈.NET委托:一个C#睡前的故事_NETWorker peter = new Worker();    
讲故事谈.NET委托:一个C#睡前的故事_NETBoss boss = new Boss();    
讲故事谈.NET委托:一个C#睡前的故事_NETpeter.Advise(boss);    
讲故事谈.NET委托:一个C#睡前的故事_NETpeter.DoWork();    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine(“Main: 工人工作完成 ”);    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.ReadLine();    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NET}
讲故事谈.NET委托:一个C#睡前的故事_NET
 
接口
现在,彼得成了一个特殊的人,他不但能容忍吝啬的老板,而且和他周围的宇宙也有了密切的联系,以至于他认为宇宙对他的工作进度也感兴趣。不幸的是,他必须也给宇宙添加一个特殊的回调函数 Advise来实现同时向他老板和宇宙报告工作进度。彼得想要把潜在的通知的列表和这些通知的实现方法分离开来,于是他决定把方法分离为一个接口:
讲故事谈.NET委托:一个C#睡前的故事_NETinterface IWorkerEvents {    
讲故事谈.NET委托:一个C#睡前的故事_NETvoid WorkStarted();    
讲故事谈.NET委托:一个C#睡前的故事_NETvoid WorkProgressing();    
讲故事谈.NET委托:一个C#睡前的故事_NETint WorkCompleted();    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NETclass Worker {    
讲故事谈.NET委托:一个C#睡前的故事_NETpublic void Advise(IWorkerEvents events) { _events = events; }    
讲故事谈.NET委托:一个C#睡前的故事_NETpublic void DoWork() {    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine(“ 工作 : 工作开始 ”);    
讲故事谈.NET委托:一个C#睡前的故事_NETif( _events != null ) _events.WorkStarted();    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine(“ 工作 : 工作进行中 ”);    
讲故事谈.NET委托:一个C#睡前的故事_NETif(_events != null ) _events.WorkProgressing();    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine("“ 工作 : 工作完成 ”");    
讲故事谈.NET委托:一个C#睡前的故事_NETif(_events != null ) {    
讲故事谈.NET委托:一个C#睡前的故事_NETint grade = _events.WorkCompleted();    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine(“ 工人的工作得分= ” + grade);    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NETprivate IWorkerEvents _events;    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NETclass Boss : IWorkerEvents {    
讲故事谈.NET委托:一个C#睡前的故事_NETpublic void WorkStarted() { /* 老板不关心。 */ }    
讲故事谈.NET委托:一个C#睡前的故事_NETpublic void WorkProgressing() { /* 老板不关心。 */ }    
讲故事谈.NET委托:一个C#睡前的故事_NETpublic int WorkCompleted() {    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine(“ 时间差不多! ”);    
讲故事谈.NET委托:一个C#睡前的故事_NETreturn 3; /* 总分为 10 */    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NET}
讲故事谈.NET委托:一个C#睡前的故事_NET
委托
不幸的是,每当彼得忙于通过接口的实现和老板交流时,就没有机会及时通知宇宙了。至少他应该忽略身在远方的老板的引用,好让其他实现了 IWorkerEvents的对象得到他的工作报告。( ”At least he'd abstracted the reference of his boss far away from him so that others who implemented the IWorkerEvents interface could be notified of his work progress” 原话如此,不理解到底是什么意思 )
他的老板还是抱怨得很厉害。 “彼得! ”他老板吼道, “你为什么在工作一开始和工作进行中都来烦我?!我不关心这些事件。你不但强迫我实现了这些方法,而且还在浪费我宝贵的工作时间来处理你的事件,特别是当我外出的时候更是如此!你能不能不再来烦我? ”
于是,彼得意识到接口虽然在很多情况都很有用,但是当用作事件时, “粒度 ”不够好。他希望能够仅在别人想要时才通知他们,于是他决定把接口的方法分离为单独的委托,每个委托都像一个小的接口方法:
讲故事谈.NET委托:一个C#睡前的故事_NETdelegate void WorkStarted();    
讲故事谈.NET委托:一个C#睡前的故事_NETdelegate void WorkProgressing();    
讲故事谈.NET委托:一个C#睡前的故事_NETdelegate int WorkCompleted();    
讲故事谈.NET委托:一个C#睡前的故事_NETclass Worker {    
讲故事谈.NET委托:一个C#睡前的故事_NETpublic void DoWork() {    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine(“ 工作 : 工作开始 ”);    
讲故事谈.NET委托:一个C#睡前的故事_NETif( started != null ) started();    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine(“ 工作 : 工作进行中 ”);    
讲故事谈.NET委托:一个C#睡前的故事_NETif( progressing != null ) progressing();    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine("“ 工作 : 工作完成 ”");    
讲故事谈.NET委托:一个C#睡前的故事_NETif( completed != null ) {    
讲故事谈.NET委托:一个C#睡前的故事_NETint grade = completed();    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine(“ 工人的工作得分= ” + grade);    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NETpublic WorkStarted started;    
讲故事谈.NET委托:一个C#睡前的故事_NETpublic WorkProgressing progressing;    
讲故事谈.NET委托:一个C#睡前的故事_NETpublic WorkCompleted completed;    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NETclass Boss {    
讲故事谈.NET委托:一个C#睡前的故事_NETpublic int WorkCompleted() {    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine("Better...");    
讲故事谈.NET委托:一个C#睡前的故事_NETreturn 4; /* 总分为 10 */    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NETclass Universe {    
讲故事谈.NET委托:一个C#睡前的故事_NETstatic void Main() {    
讲故事谈.NET委托:一个C#睡前的故事_NETWorker peter = new Worker();    
讲故事谈.NET委托:一个C#睡前的故事_NETBoss boss = new Boss();    
讲故事谈.NET委托:一个C#睡前的故事_NETpeter.completed = new WorkCompleted(boss.WorkCompleted);    
讲故事谈.NET委托:一个C#睡前的故事_NETpeter.DoWork();    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine(“Main: 工人工作完成 ”);    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.ReadLine();    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NET}
 静态监听者
这样,彼得不会再拿他老板不想要的事件来烦他老板了,但是他还没有把宇宙放到他的监听者列表中。因为宇宙是个包涵一切的实体,看来不适合使用实例方法的委托(想像一下,实例化一个 “宇宙 ”要花费多少资源 …..),于是彼得就需要能够对静态委托进行挂钩,委托对这一点支持得很好:
讲故事谈.NET委托:一个C#睡前的故事_NET            class Universe {    
讲故事谈.NET委托:一个C#睡前的故事_NETstatic void WorkerStartedWork() {    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine("Universe notices worker starting work");    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NETstatic int WorkerCompletedWork() {    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine("Universe pleased with worker's work");    
讲故事谈.NET委托:一个C#睡前的故事_NETreturn 7;    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NETstatic void Main() {    
讲故事谈.NET委托:一个C#睡前的故事_NETWorker peter = new Worker();    
讲故事谈.NET委托:一个C#睡前的故事_NETBoss boss = new Boss();    
讲故事谈.NET委托:一个C#睡前的故事_NETpeter.completed = new WorkCompleted(boss.WorkCompleted);    
讲故事谈.NET委托:一个C#睡前的故事_NETpeter.started = new WorkStarted(Universe.WorkerStartedWork);    
讲故事谈.NET委托:一个C#睡前的故事_NETpeter.completed = new WorkCompleted(Universe.WorkerCompletedWork);    
讲故事谈.NET委托:一个C#睡前的故事_NETpeter.DoWork();    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine(“Main: 工人工作完成 ”);    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.ReadLine();    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NET}
 
事件
不幸的是,宇宙太忙了,也不习惯时刻关注它里面的个体,它可以用自己的委托替换了彼得老板的委托。这是把彼得的 Worker类的的委托字段做成 public的一个无意识的副作用。同样,如果彼得的老板不耐烦了,也可以决定自己来激发彼得的委托(真是一个粗鲁的老板):
// Peter's boss taking matters into his own hands
if( peter.completed != null ) peter.completed();
彼得不想让这些事发生,他意识到需要给每个委托提供 “注册 ”和 “反注册 ”功能,这样监听者就可以自己添加和移除委托,但同时又不能清空整个列表也不能随意激发彼得的事件了。彼得并没有来自己实现这些功能,相反,他使用了 event关键字让 C#编译器为他构建这些方法:
讲故事谈.NET委托:一个C#睡前的故事_NETclass Worker {    
讲故事谈.NET委托:一个C#睡前的故事_NET...    
讲故事谈.NET委托:一个C#睡前的故事_NETpublic event WorkStarted started;    
讲故事谈.NET委托:一个C#睡前的故事_NETpublic event WorkProgressing progressing;    
讲故事谈.NET委托:一个C#睡前的故事_NETpublic event WorkCompleted completed;    
讲故事谈.NET委托:一个C#睡前的故事_NET}
讲故事谈.NET委托:一个C#睡前的故事_NET彼得知道 event关键字在委托的外边包装了一个 property,仅让 C#客户通过 += 和 -=操作符来添加和移除,强迫他的老板和宇宙正确地使用事件。
讲故事谈.NET委托:一个C#睡前的故事_NETstatic void Main() {    
讲故事谈.NET委托:一个C#睡前的故事_NETWorker peter = new Worker();    
讲故事谈.NET委托:一个C#睡前的故事_NETBoss boss = new Boss();    
讲故事谈.NET委托:一个C#睡前的故事_NETpeter.completed += new WorkCompleted(boss.WorkCompleted);    
讲故事谈.NET委托:一个C#睡前的故事_NETpeter.started += new WorkStarted(Universe.WorkerStartedWork);    
讲故事谈.NET委托:一个C#睡前的故事_NETpeter.completed += new WorkCompleted(Universe.WorkerCompletedWork);    
讲故事谈.NET委托:一个C#睡前的故事_NETpeter.DoWork();    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine(“Main: 工人工作完成 ”);    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.ReadLine();    
讲故事谈.NET委托:一个C#睡前的故事_NET}
“ 收获 ”所有结果
到这时,彼得终于可以送一口气了,他成功地满足了所有监听者的需求,同时避免了与特定实现的紧耦合。但是他注意到他的老板和宇宙都为它的工作打了分,但是他仅仅接收了一个分数。面对多个监听者,他想要 “收获 ”所有的结果,于是他深入到代理里面,轮询监听者列表,手工一个个调用:
讲故事谈.NET委托:一个C#睡前的故事_NET public void DoWork() {    
讲故事谈.NET委托:一个C#睡前的故事_NET...    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine("“ 工作 : 工作完成 ”");    
讲故事谈.NET委托:一个C#睡前的故事_NETif( completed != null ) {    
讲故事谈.NET委托:一个C#睡前的故事_NETforeach( WorkCompleted wc in completed.GetInvocationList() ) {    
讲故事谈.NET委托:一个C#睡前的故事_NETint grade = wc();    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine(“ 工人的工作得分= ” + grade);    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NET}
 异步通知:激发 & 忘掉
同时,他的老板和宇宙还要忙于处理其他事情,也就是说他们给彼得打分所花费的事件变得非常长:
讲故事谈.NET委托:一个C#睡前的故事_NETclass Boss {    
讲故事谈.NET委托:一个C#睡前的故事_NETpublic int WorkCompleted() {    
讲故事谈.NET委托:一个C#睡前的故事_NETSystem.Threading.Thread.Sleep(3000);    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine("Better..."); return 6; /* 总分为 10 */    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NETclass Universe {    
讲故事谈.NET委托:一个C#睡前的故事_NETstatic int WorkerCompletedWork() {    
讲故事谈.NET委托:一个C#睡前的故事_NETSystem.Threading.Thread.Sleep(4000);    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine("Universe is pleased with worker's work");    
讲故事谈.NET委托:一个C#睡前的故事_NETreturn 7;    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NET...    
讲故事谈.NET委托:一个C#睡前的故事_NET}
很不幸,彼得每次通知一个监听者后必须等待它给自己打分,现在这些通知花费了他太多的工作事件。于是他决定忘掉分数,仅仅异步激发事件:
讲故事谈.NET委托:一个C#睡前的故事_NET public void DoWork() {    
讲故事谈.NET委托:一个C#睡前的故事_NET...    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine("“ 工作 : 工作完成 ”");    
讲故事谈.NET委托:一个C#睡前的故事_NETif( completed != null ) {    
讲故事谈.NET委托:一个C#睡前的故事_NETforeach( WorkCompleted wc in completed.GetInvocationList() )    
讲故事谈.NET委托:一个C#睡前的故事_NET{    
讲故事谈.NET委托:一个C#睡前的故事_NETwc.BeginInvoke(null, null);    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NET}
异步通知:轮询
这使得彼得可以通知他的监听者,然后立即返回工作,让进程的线程池来调用这些代理。随着时间的过去,彼得发现他丢失了他工作的反馈,他知道听取别人的赞扬和努力工作一样重要,于是他异步激发事件,但是周期性地轮询,取得可用的分数。
讲故事谈.NET委托:一个C#睡前的故事_NET public void DoWork() {    
讲故事谈.NET委托:一个C#睡前的故事_NET...    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine("“ 工作 : 工作完成 ”");    
讲故事谈.NET委托:一个C#睡前的故事_NETif( completed != null ) {    
讲故事谈.NET委托:一个C#睡前的故事_NETforeach( WorkCompleted wc in completed.GetInvocationList() ) {    
讲故事谈.NET委托:一个C#睡前的故事_NETIAsyncResult res = wc.BeginInvoke(null, null);    
讲故事谈.NET委托:一个C#睡前的故事_NETwhile( !res.IsCompleted ) System.Threading.Thread.Sleep(1);    
讲故事谈.NET委托:一个C#睡前的故事_NETint grade = wc.EndInvoke(res);    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine(“ 工人的工作得分= ” + grade);    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NET}
 异步通知:委托
不幸地,彼得有回到了一开始就想避免的情况中来,比如,老板站在背后盯着他工作。于是,他决定使用自己的委托作为他调用的异步委托完成的通知,让他自己立即回到工作,但是仍可以在别人给他的工作打分后得到通知:
讲故事谈.NET委托:一个C#睡前的故事_NET            public void DoWork() {    
讲故事谈.NET委托:一个C#睡前的故事_NET...    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine("“ 工作 : 工作完成 ”");    
讲故事谈.NET委托:一个C#睡前的故事_NETif( completed != null ) {    
讲故事谈.NET委托:一个C#睡前的故事_NETforeach( WorkCompleted wc in completed.GetInvocationList() ) {    
讲故事谈.NET委托:一个C#睡前的故事_NETwc.BeginInvoke(new AsyncCallback(WorkGraded), wc);    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NET}    
讲故事谈.NET委托:一个C#睡前的故事_NETprivate void WorkGraded(IAsyncResult res) {    
讲故事谈.NET委托:一个C#睡前的故事_NETWorkCompleted wc = (WorkCompleted)res.AsyncState;    
讲故事谈.NET委托:一个C#睡前的故事_NETint grade = wc.EndInvoke(res);    
讲故事谈.NET委托:一个C#睡前的故事_NETConsole.WriteLine(“ 工人的工作得分= ” + grade);    
讲故事谈.NET委托:一个C#睡前的故事_NET}
宇宙中的幸福
彼得、他的老板和宇宙最终都满足了。彼得的老板和宇宙可以收到他们感兴趣的事件通知,减少了实现的负担和非必需的往返 “差旅费 ”。彼得可以通知他们,而不管他们要花多长时间来从目的方法中返回,同时又可以异步地得到他的结果。彼得知道,这并不 *十分 *简单,因为当他异步激发事件时,方法要在另外一个线程中执行,彼得的目的方法完成的通知也是一样的道理。但是,迈克和彼得是好朋友,他很熟悉线程的事情,可以在这个领提供指导。
他们永远幸福地生活下去 ……<完 >