.NET委托:一个C#睡前故事(中英双语版)

.NET委托:一个C#睡前故事

英文版原作者:Chris Sells(www.sellsbrothers.com

翻译:袁晓辉(www.farproc.com http://blog.csdn.net/uoyevoli



The following is an excerpt from WindowsForms 2.0 Programming, Chris Sells & Michael Weinhardt, Addison-Wesley,2006. It's been updated from the original version for C# 2.0.

 

Once upon a time, in a strange land southof here, there was a worker named Peter. He was a diligent worker who wouldreadily accept requests from his boss. However, his boss was a mean, untrustingman who insisted on steady progress reports. Since Peter did not want his bossstanding in his office looking over his shoulder, Peter promised to notify hisboss whenever his work progressed. Peter implemented this promise byperiodically calling his boss back via a typed reference like so:

class Worker {
 Boss boss;
 
 public void Advise(Boss boss) {
    this.boss = boss;
  }
 
 public void DoWork() {
    Console.WriteLine("Worker: work started");
    if( this.boss != null ) this.boss.WorkStarted();
 
    Console.WriteLine("Worker: work progressing");
    if( this.boss != null ) this.boss.WorkProgressing();
 
    Console.WriteLine("Worker: work completed");
    if( this.boss != null ) {
      int grade = this.boss.WorkCompleted();
      Console.WriteLine("Worker grade= {0}", grade);
    }
  }
}
 
class Boss {
 public void WorkStarted() {
    // Boss doesn't care
  }
 public void WorkProgressing() {
    // Boss doesn't care
  }
 public int WorkCompleted() {
    Console.WriteLine("It's about time!");
    return 2; // out of 10
  }
}
 
class Universe {
 static void Main() {
    Worker peter = new Worker();
    Boss boss = new Boss();
    peter.Advise(boss);
    peter.DoWork();
 
    Console.WriteLine("Main: worker completed work");
    Console.ReadLine();
  }
}

Interfaces

Now Peter was a special person. Not onlywas he able to put up with his mean-spirited boss, but he also had a deepconnection with the universe around him. So much so that he felt that theuniverse was interested in his progress. Unfortunately, there was no way forPeter to advise the Universe of his progress unless he added a special Advisemethod and special callbacks just for the Universe, in addition to keeping hisboss informed. What Peter really wanted to do was to separate the list ofpotential notifications from the implementation of those notification methods.And so he decided to split the methods into an interface:

interface IWorkerEvents {
 void WorkStarted();
 void WorkProgressing();
  intWorkCompleted();
}
 
class Worker {
 IWorkerEvents events;
 
 public void Advise(IWorkerEvents events) {
    this.events = events;
  }
 
 public void DoWork() {
    Console.WriteLine("Worker: work started");
    if( this.events != null ) this.events.WorkStarted();
 
    Console.WriteLine("Worker: work progressing");
    if( this.events != null ) this.events.WorkProgressing();
 
    Console.WriteLine("Worker: work completed");
    if( this.events!= null ) {
      int grade = this.events.WorkCompleted();
      Console.WriteLine("Worker grade= {0}", grade);
    }
  }
}
 
class Boss : IWorkerEvents {
 public void WorkStarted() {
    // Boss doesn't care
  }
 public void WorkProgressing() {
    // Boss doesn't care
  }
 public int WorkCompleted() {
    Console.WriteLine("It's about time!");
    return 3; // out of 10
  }
}

Delegates

Unfortunately, Peter was so busy talkinghis boss into implementing this interface that he didn't get around tonotifying the Universe, but he knew he would soon. At least he'd abstracted thereference of his boss far away from him so that others who implemented theIWorkerEvents interface could be notified of his work progress.

 

Still, his boss complained bitterly."Peter!" his boss fumed. "Why are you bothering to notify mewhen you start your work or when your work is progressing?!? I don't care aboutthose events. Not only do you force me to implement those methods, but you'rewasting valuable work time waiting for me to return from the event, which isfurther expanded when I am far away! Can't you figure out a way to stopbothering me?"

 

And so, Peter decided that while interfaceswere useful for many things, when it came to events, their granularity was notfine enough. He wished to be able to notify interested parties only of theevents that matched their hearts' desires. So, he decided to break the methodsout of the interface into separate delegate functions, each of which acted likea little tiny interface of one method each:

delegate void WorkStarted();
delegate void WorkProgressing();
delegate int WorkCompleted();
 
class Worker {
 public WorkStarted Started;
 public WorkProgressing Progressing;
  publicWorkCompleted Completed;
 
 public void DoWork() {
    Console.WriteLine("Worker: work started");
    if( this.Started != null ) this.Started();
 
    Console.WriteLine("Worker: work progressing");
    if( this.Progressing != null ) this.Progressing();
 
    Console.WriteLine("Worker: work completed");
    if( this.Completed != null ) {
      int grade = this.Completed();
      Console.WriteLine("Worker grade= {0}", grade);
    }
  }
}
 
class Boss {
 public int WorkCompleted() {
    Console.WriteLine("It's about time!");
    return 4; // out of 10
  }
}
 
class Universe {
 static void Main() {
    Worker peter = new Worker();
    Boss boss = new Boss();
 
    // NOTE: We've replaced the Advise method with the assignment operation
    peter.Completed = new WorkCompleted(boss.WorkCompleted);
    peter.DoWork();
 
    Console.WriteLine("Main: worker completed work");
    Console.ReadLine();
  }
}

And, because Peter was under so muchpressure, he decided to advantage of the shorthand notation for assigningdelegates provided by C# 2.0:

class Universe {
 static void Main() {
    ...
    peter.Completed = boss.WorkCompleted;
    ...
  }
}

Static Listeners

Delegates accomplished the goal of notbothering his boss with events that he didn't want, but still Peter had notmanaged to get the universe on his list of listeners. Since the universe is anall-encompassing entity, it didn't seem right to hook delegates to instancemembers (imagine how many resources multiple instances of the universe wouldneed...). Instead, Peter need to hook delegates to static members, whichdelegates support fully:

class Universe {
 static void WorkerStartedWork() {
    Console.WriteLine("Universe notices worker starting work");
  }
 
 static int WorkerCompletedWork() {
    Console.WriteLine("Universe pleased with worker's work");
    return 7;
  }
 
 static void Main() {
    Worker peter = new Worker();
    Boss boss = new Boss();
 
    peter.Completed = boss.WorkCompleted;
    peter.Started = WorkerStartedWork;
    peter.Completed = WorkerCompletedWork; // Oops!
    peter.DoWork();
 
    Console.WriteLine("Main: worker completed work");
    Console.ReadLine();
  }
}

Events

Unfortunately, the Universe being very busyand unaccustomed to paying attention to individuals, has managed to replacePeter's boss's delegate with its own. This is an unintended side effect ofmaking the delegate fields public in Peter's Worker class. Likewise, if Peter'sboss gets impatient, he can decide to fire Peter's delegates himself (which isjust the kind of rude thing that Peter's boss was apt to do):

// Peter's boss taking matters into his ownhands
if( peter.Completed != null )peter.Completed();

Peter wants to make sure that neither ofthese can happens. He realizes he needs to add registration and unregistrationfunctions for each delegate so that listeners can add or remove themselves, butcan't clear the entire list or fire Peter's events. Instead of implementingthese functions himself, Peter uses the event keyword to make the C# compilerbuild these methods for him:

class Worker {
 public event WorkStarted Started;
 public event WorkProgressing Progressing;
 public event WorkCompleted Completed;
  ...
}

Peter knows that the event keyword erects aproperty around a delegate, only allowing clients to add or remove themselves(using the += and -= operators in C#), forcing his boss and the universe toplay nicely:

class Universe {
  ...
 static void Main() {
    Worker peter = new Worker();
    Boss boss = new Boss();
 
    peter.Completed = boss.WorkCompleted; // ERR!
    peter.Completed += boss.WorkCompleted; // OK
    peter.Started += Universe.WorkerStartedWork; // OK
    peter.Completed += Universe.WorkerCompletedWork; // OK
 
    peter.DoWork();
 
    Console.WriteLine("Main: worker completed work");
    Console.ReadLine();
  }
}

Harvesting All Results

At this point, Peter breathes a sigh ofrelief. He has managed to satisfy the requirements of all his listeners withouthaving to be closely coupled with the specific implementations. However, henotices that while both his boss and the universe provide grades of his workthat he's only receiving one of the grades. In the face of multiple listeners,he'd really like to harvest all of their results. So, he reaches into hisdelegate and pulls out the list of listeners so that he can call each of themmanually:

class Worker {
  ...
 public void DoWork() {
    ...
    Console.WriteLine("Worker: work completed");
 
    if( this.Completed != null ) {
      foreach( WorkCompleted wc in this.Completed.GetInvocationList() ) {
        int grade = wc();
        Console.WriteLine("Worker grade= {0}", grade);
      }
    }
  }
}
 
public void DoWork() {
  ...
 Console.WriteLine("Worker: work completed");
  if(completed != null ) {
    foreach( WorkCompleted wc in completed.GetInvocationList() ) {
      int grade = wc();
      Console.WriteLine("Worker grade= " + grade);
    }
  }
}

Asynchronous Notification: Fire &Forget

In the meantime, his boss and the universehave been distracted with other things, which meant that the time it takes themto grade Peter's work is greatly expanded:

class Boss {
 public int WorkCompleted() {
    System.Threading.Thread.Sleep(5000);
    Console.WriteLine("Better...");
    return 4; // out of 10
  }
}
 
class Universe {
  ...
 static int WorkerCompletedWork() {
    System.Threading.Thread.Sleep(1000000);
    Console.WriteLine("Universe pleased with worker's work");
    return 7;
  }
  ...
}

Unfortunately, since Peter is notifyingeach listener one at a time, waiting for each to grade him, these notificationsnow take up quite a bit of his time when he should be working. So, he decidesto forget the grade and just fire the event asynchronously:

class Worker {
  ...
 public void DoWork() {
    ...
    Console.WriteLine("Worker: work completed");
    if( this.Completed != null ) {
      foreach( WorkCompleted wc in this.Completed.GetInvocationList() ) {
        wc.BeginInvoke(null, null); // EndInvoke call required by .NET
      }
    }
  }
}

Asynchronous Notification: Polling

The call to BeginInvoke allows Peter tonotify the listeners while letting Peter get back to work immediately, lettingthe process thread pool invoke the delegate. Over time, however, Peter findsthat he misses the feedback on his work. He knows that he does a good job andappreciates the praise of the universe as a whole (if not his bossspecifically). Plus, he s afraid he s leaking .NET resources acquired bycalling BeginInvoke without calling the corresponding EndInvoke method, so, hefires the event asynchronously, but polls periodically, looking for the gradeto be available:

class Worker {
  ...
 public void DoWork() {
    ...
    Console.WriteLine("Worker: work completed");
    if( this.Completed != null ) {
      foreach( WorkCompleted wc in this.Completed.GetInvocationList() ) {
        IAsyncResult result = wc.BeginInvoke(null, null);
        while( !result.IsCompleted ) System.Threading.Thread.Sleep(1);
        int grade = wc.EndInvoke(result);
       Console.WriteLine("Worker grade= {0}", grade);
      }
    }
  }
}

Asynchronous Notification: Delegates

Unfortunately, Peter is back to what hewanted his boss to avoid with him in the beginning, i.e. looking over theshoulder of the entity doing the work. So, he decides to employ his owndelegate as a means of notification when the asynchronous work has completed,allowing him to get back to work immediately, but still be notified when hiswork has been graded:

class Worker {
  ...
 public void DoWork() {
    ...
    Console.WriteLine("Worker: work completed");
    if( this.Completed != null ) {
      foreach( WorkCompleted wc in this.Completed.GetInvocationList() ) {
        wc.BeginInvoke(this.WorkGraded, wc);
      }
    }
  }
 
 void WorkGraded(IAsyncResult result) {
    WorkCompleted wc = (WorkCompleted)result.AsyncState;
    int grade = wc.EndInvoke(result);
    Console.WriteLine("Worker grade= {0}" + grade);
  }
}

Anonymous Delegates

At this point, Peter is using delegates tonotify interested parties in the process of his work and using delegates to getnotified when grades are available on the work he s completed. The delegatesprovided by his boss and the universe are provided by separate entities, so itmakes sense that they are encapsulated in methods on those entities. However,in the case of the WorkGraded method, there s really no good reason for this tobe a separate method except the syntactic requirements of C# 1.0. As of C# 2.0,Peter can drop the code required to handle the processing of his work gradeinto an anonymous delegate:

class Worker {
  ...
 public void DoWork() {
    ...
    Console.WriteLine("Worker: work completed");
    if( this.Completed != null ) {
      foreach( WorkCompleted wc in this.Completed.GetInvocationList() ) {
        wc.BeginInvoke(delegate(IAsyncResult result) {
          WorkCompleted wc2 = (WorkCompleted)result.AsyncState;
          int grade = wc2.EndInvoke(result);
          Console.WriteLine("Worker grade= {0}", grade);
        },
        wc);
      }
    }
  }
}

Here, instead of passing in the name of amethod to call when his work has been graded, he s passing in the body of themethod itself as designated with a different use of the delegate keyword tocreate a method with no name (and therefore anonymous ). The body of the method is fundamentally the same in thatPeter still passes the WorkCompleted delegate as a parameter to BeginInvoke andthen pulls it out of AsyncState for use in extracting the result. However, oneof the benefits of anonymous delegates that Peter knows is that he can make useof the variables in the surrounding context from within the anonymous delegatebody, causing him to rewrite his code thusly:

class Worker {
  ...
 public void DoWork() {
    ...
    Console.WriteLine("Worker: work completed");
    if( this.Completed != null ) {
      foreach( WorkCompleted wc in this.Completed.GetInvocationList() ) {
        wc.BeginInvoke(delegate(IAsyncResult result) {
          // Use wc variable from surrounding context (ERR!)
          int grade = wc.EndInvoke(result);
          Console.WriteLine("Worker grade= {0}", grade);
        },
        null);
      }
    }
  }
}

This code compiles just fine, but when it srun, it will cause the following exception to be thrown:

 

System.InvalidOperationException:

  TheIAsyncResult object provided does not match this delegate.

 

The problem is that while the wc variableis allowed to be used in the anonymous delegate, it s still being used by thefor-each statement. As soon as the asynchronous invocation begins, the wcvariable changes and the delegate used to start things (wc) no longer matchesthe async result passed as an argument to the anonymous delegate. Peter slaps hishead and creates a hybrid solution:

class Worker {
  ...
 public void DoWork() {
    ...
    Console.WriteLine("Worker: work completed");
    if( this.Completed != null ) {
      foreach( WorkCompleted wc in this.Completed.GetInvocationList() ) {
        // Create an unchanging variable referencing the current delegate
        WorkCompleted wc2 = wc;
        wc.BeginInvoke(delegate(IAsyncResult result) {
          // Use wc2 variable from surrounding context
          int grade = wc2.EndInvoke(result);
          Console.WriteLine("Worker grade= {0}", grade);
        },
        null);
      }
    }
  }
}
 
public void DoWork() {
  ...
 Console.WriteLine("Worker: work completed");
  if(completed != null ) {
    foreach( WorkCompleted wc in completed.GetInvocationList() ) {
      wc.BeginInvoke(new AsyncCallback(WorkGraded), wc);
    }
  }
}
 
void WorkGraded(IAsyncResult res) {
 WorkCompleted wc = (WorkCompleted)res.AsyncState;
  intgrade = wc.EndInvoke(res);
 Console.WriteLine("Worker grade= " + grade);
}

Happiness in the Universe

 

Peter, his boss and the universe arefinally satisfied. Peter's boss and the universe are allowed to be notified ofthe events that interest them, reducing the burden of implementation and thecost of unnecessary round-trips. Peter can notify them each, ignoring how longit takes them to return from their target methods, while still getting hisresults asynchronously and handling them using anonymous delegates, resultingin the following complete solution:

delegate void WorkStarted();
delegate void WorkProgressing();
delegate int WorkCompleted();
 
class Worker {
 public event WorkStarted Started;
 public event WorkProgressing Progressing;
 public event WorkCompleted Completed;
 
 public void DoWork() {
    Console.WriteLine("Worker: work started");
    if( this.Started != null )
      this.Started();
 
    Console.WriteLine("Worker: work progressing");
    if( this.Progressing != null )
      this.Progressing();
 
    Console.WriteLine("Worker: work completed");
    if( this.Completed != null ) {
      foreach( WorkCompleted wc in this.Completed.GetInvocationList() ) {
        WorkCompleted wc2 = wc;
        wc.BeginInvoke(delegate(IAsyncResult result) {
          int grade = wc2.EndInvoke(result);
          Console.WriteLine("Worker grade= {0}", grade);
        },
        null);
      }
    }
  }
}
 
class Boss {
 public int WorkCompleted() {
    System.Threading.Thread.Sleep(3000);
    Console.WriteLine("Better...");
    return 5; // out of 10
  }
}
 
class Universe {
 static void WorkerStartedWork() {
    Console.WriteLine("Universe notices worker starting work");
  }
 
 static int WorkerCompletedWork() {
    System.Threading.Thread.Sleep(4000);
    Console.WriteLine("Universe pleased with worker's work");
    return 7;
  }
 
 static void Main() {
    Worker peter = new Worker();
    Boss boss = new Boss();
    peter.Completed += boss.WorkCompleted;
    peter.Started += Universe.WorkerStartedWork;
    peter.Completed += Universe.WorkerCompletedWork;
    peter.DoWork();
 
    Console.WriteLine("Main: worker completed work");
  }
}

Peter knows that getting resultsasynchronously comes with issues, because as soon as he fires eventsasynchronously, the target methods are likely to be executed on another thread,as is Peter's notification of when the target method has completed. However,Peter is good friends with Mike, who is very familiar with threading issues andcan provide guidance in that area.

 

And they all lived happily every after.

 

The end.

=======================================================================================================

紧耦合

       从前,在南方一块奇异的土地上,有个工人名叫彼得,他非常勤奋,对他的老板总是百依百顺。但是他的老板是个吝啬的人,从不信任别人,坚决要求随时知道彼得的工作进度,以防止他偷懒。但是彼得又不想让老板呆在他的办公室里站在背后盯着他,于是就对老板做出承诺:无论何时,只要我的工作取得了一点进展我都会及时让你知道。彼得通过周期性地使用“带类型的引用”(原文为:“typed reference” 也就是delegate??)“回调”他的老板来实现他的承诺,如下:

class Worker {
    public void Advise(Boss boss) { _boss = boss; }
    public void DoWork() {
        Console.WriteLine(“工作: 工作开始”);
        if( _boss != null ) _boss.WorkStarted();
 
        Console.WriteLine(“工作: 工作进行中”);
        if( _boss != null ) _boss.WorkProgressing();
 
        Console.WriteLine("“工作: 工作完成”");
        if( _boss != null ) {
            int grade = _boss.WorkCompleted();
            Console.WriteLine(“工人的工作得分=” + grade);
        }
    }
    private Boss _boss;
}
 
class Boss {
    public void WorkStarted() { /* 老板不关心。 */ }
    public void WorkProgressing() { /*老板不关心。 */ }
    public int WorkCompleted() {
        Console.WriteLine(“时间差不多!”);
        return 2; /* 总分为10 */
    }
}
 
class Universe {
    static void Main() {
        Worker  peter = new Worker();
        Boss        boss = new Boss();
        peter.Advise(boss);
        peter.DoWork();
 
        Console.WriteLine(“Main: 工人工作完成”);
        Console.ReadLine();
    }
}

接口

     现在,彼得成了一个特殊的人,他不但能容忍吝啬的老板,而且和他周围的宇宙也有了密切的联系,以至于他认为宇宙对他的工作进度也感兴趣。不幸的是,他必须也给宇宙添加一个特殊的回调函数Advise来实现同时向他老板和宇宙报告工作进度。彼得想要把潜在的通知的列表和这些通知的实现方法分离开来,于是他决定把方法分离为一个接口:
interface IWorkerEvents {
    void WorkStarted();
    void WorkProgressing();
    int WorkCompleted();
}
 
class Worker {
    public void Advise(IWorkerEvents events) { _events = events; }
    public void DoWork() {
        Console.WriteLine(“工作: 工作开始”);
        if( _events != null ) _events.WorkStarted();
 
        Console.WriteLine(“工作: 工作进行中”);
        if(_events != null ) _events.WorkProgressing();
 
        Console.WriteLine("“工作: 工作完成”");
        if(_events != null ) {
            int grade = _events.WorkCompleted();
 
            Console.WriteLine(“工人的工作得分=” + grade);
        }
    }
    private IWorkerEvents _events;
}
 
class Boss : IWorkerEvents {
    public void WorkStarted() { /* 老板不关心。 */ }
    public void WorkProgressing() { /* 老板不关心。 */ }
    public int WorkCompleted() {
        Console.WriteLine(“时间差不多!”);

        return 3; /* 总分为10 */
    }
}

委托

     不幸的是,每当彼得忙于通过接口的实现和老板交流时,就没有机会及时通知宇宙了。至少他应该忽略身在远方的老板的引用,好让其他实现了IWorkerEvents的对象得到他的工作报告。(”Atleast he'd abstracted the reference of his boss far away from him so thatothers who implemented the IWorkerEvents interface could be notified of hiswork progress” 原话如此,不理解到底是什么意思:))

     他的老板还是抱怨得很厉害。“彼得!”他老板吼道,“你为什么在工作一开始和工作进行中都来烦我?!我不关心这些事件。你不但强迫我实现了这些方法,而且还在浪费我宝贵的工作时间来处理你的事件,特别是当我外出的时候更是如此!你能不能不再来烦我?”

     于是,彼得意识到接口虽然在很多情况都很有用,但是当用作事件时,“粒度”不够好。他希望能够仅在别人想要时才通知他们,于是他决定把接口的方法分离为单独的委托,每个委托都像一个小的接口方法:

delegate void WorkStarted();
delegate void WorkProgressing();
delegate int WorkCompleted();
 
class Worker {
    public void DoWork() {
        Console.WriteLine(“工作: 工作开始”);
        if( started != null ) started();
 
        Console.WriteLine(“工作: 工作进行中”);
        if( progressing != null ) progressing();
 
        Console.WriteLine("“工作: 工作完成”");
        if( completed != null ) {
            int grade = completed();
            Console.WriteLine(“工人的工作得分=” + grade);
        }
    }
    public WorkStarted started;
    public WorkProgressing progressing;
    public WorkCompleted completed;
}
 
class Boss {
    public int WorkCompleted() {
        Console.WriteLine("Better...");
        return 4; /* 总分为10 */
    }
}
 
class Universe {
    static void Main() {
        Worker  peter = new Worker();
        Boss        boss = new Boss();
        peter.completed = new WorkCompleted(boss.WorkCompleted);
        peter.DoWork();
 
        Console.WriteLine(“Main: 工人工作完成”);
        Console.ReadLine();
    }
}

静态监听者

      这样,彼得不会再拿他老板不想要的事件来烦他老板了,但是他还没有把宇宙放到他的监听者列表中。因为宇宙是个包涵一切的实体,看来不适合使用实例方法的委托(想像一下,实例化一个“宇宙”要花费多少资源…..),于是彼得就需要能够对静态委托进行挂钩,委托对这一点支持得很好:
class Universe {
    static void WorkerStartedWork() {
        Console.WriteLine("Universe notices worker starting work");
    }
 
    static int WorkerCompletedWork() {
        Console.WriteLine("Universe pleased with worker's work");
        return 7;
    }
 
    static void Main() {
        Worker  peter = new Worker();
        Boss        boss = new Boss();
        peter.completed = new WorkCompleted(boss.WorkCompleted);
        peter.started = new WorkStarted(Universe.WorkerStartedWork);
        peter.completed = new WorkCompleted(Universe.WorkerCompletedWork);
        peter.DoWork();
 
        Console.WriteLine(“Main: 工人工作完成”);
        Console.ReadLine();
    }
}

事件

      不幸的是,宇宙太忙了,也不习惯时刻关注它里面的个体,它可以用自己的委托替换了彼得老板的委托。这是把彼得的Worker类的的委托字段做成public的一个无意识的副作用。同样,如果彼得的老板不耐烦了,也可以决定自己来激发彼得的委托(真是一个粗鲁的老板):

       // Peter's boss taking matters into his own hands
       if( peter.completed != null ) peter.completed();

      彼得不想让这些事发生,他意识到需要给每个委托提供“注册”和“反注册”功能,这样监听者就可以自己添加和移除委托,但同时又不能清空整个列表也不能随意激发彼得的事件了。彼得并没有来自己实现这些功能,相反,他使用了event关键字让C#编译器为他构建这些方法:

class Worker {
...
   public event WorkStarted started;
   public event WorkProgressing progressing;
   public event WorkCompleted completed;
}

      彼得知道event关键字在委托的外边包装了一个property,仅让C#客户通过+= 和 -=操作符来添加和移除,强迫他的老板和宇宙正确地使用事件。

static void Main() {
        Worker  peter = new Worker();
        Boss        boss = new Boss();
        peter.completed += new WorkCompleted(boss.WorkCompleted);
        peter.started += new WorkStarted(Universe.WorkerStartedWork);
        peter.completed += new WorkCompleted(Universe.WorkerCompletedWork);
        peter.DoWork();
 
        Console.WriteLine(“Main: 工人工作完成”);
        Console.ReadLine();
    }

“收获”所有结果

       到这时,彼得终于可以送一口气了,他成功地满足了所有监听者的需求,同时避免了与特定实现的紧耦合。但是他注意到他的老板和宇宙都为它的工作打了分,但是他仅仅接收了一个分数。面对多个监听者,他想要“收获”所有的结果,于是他深入到代理里面,轮询监听者列表,手工一个个调用:

public void DoWork() {
        ...
        Console.WriteLine("“工作: 工作完成”");
        if( completed != null ) {
            foreach( WorkCompleted wc in completed.GetInvocationList() ) {
                int grade = wc();
                Console.WriteLine(“工人的工作得分=” + grade);
            }
        }
    }

异步通知:激发 & 忘掉

      同时,他的老板和宇宙还要忙于处理其他事情,也就是说他们给彼得打分所花费的事件变得非常长:

class Boss {
    public int WorkCompleted() {
        System.Threading.Thread.Sleep(3000);
        Console.WriteLine("Better..."); return 6; /* 总分为10 */
    }
}
 
class Universe {
    static int WorkerCompletedWork() {
        System.Threading.Thread.Sleep(4000);
        Console.WriteLine("Universe is pleased with worker's work");
        return 7;
    }
    ...
}

       很不幸,彼得每次通知一个监听者后必须等待它给自己打分,现在这些通知花费了他太多的工作事件。于是他决定忘掉分数,仅仅异步激发事件:

public void DoWork() {
        ...
        Console.WriteLine("“工作: 工作完成”");
        if( completed != null ) {
            foreach( WorkCompleted wc in completed.GetInvocationList() )
            {
                wc.BeginInvoke(null, null);
            }
        }
    }

异步通知:轮询

      这使得彼得可以通知他的监听者,然后立即返回工作,让进程的线程池来调用这些代理。随着时间的过去,彼得发现他丢失了他工作的反馈,他知道听取别人的赞扬和努力工作一样重要,于是他异步激发事件,但是周期性地轮询,取得可用的分数。

public void DoWork() {
        ...
        Console.WriteLine("“工作: 工作完成”");
        if( completed != null ) {
            foreach( WorkCompleted wc in completed.GetInvocationList() ) {
                IAsyncResult res = wc.BeginInvoke(null, null);
                while( !res.IsCompleted ) System.Threading.Thread.Sleep(1);
                int grade = wc.EndInvoke(res);
                Console.WriteLine(“工人的工作得分=” + grade);
            }
        }
    }

异步通知:委托

      不幸地,彼得有回到了一开始就想避免的情况中来,比如,老板站在背后盯着他工作。于是,他决定使用自己的委托作为他调用的异步委托完成的通知,让他自己立即回到工作,但是仍可以在别人给他的工作打分后得到通知:

public void DoWork() {
        ...
        Console.WriteLine("“工作: 工作完成”");
        if( completed != null ) {
            foreach( WorkCompleted wc in completed.GetInvocationList() ) {
                wc.BeginInvoke(new AsyncCallback(WorkGraded), wc);
            }
        }
    }
 
    private void WorkGraded(IAsyncResult res) {
        WorkCompleted wc = (WorkCompleted)res.AsyncState;
        int grade = wc.EndInvoke(res);
        Console.WriteLine(“工人的工作得分=” + grade);
    }

宇宙中的幸福

       彼得、他的老板和宇宙最终都满足了。彼得的老板和宇宙可以收到他们感兴趣的事件通知,减少了实现的负担和非必需的往返“差旅费”。彼得可以通知他们,而不管他们要花多长时间来从目的方法中返回,同时又可以异步地得到他的结果。彼得知道,这并不*十分*简单,因为当他异步激发事件时,方法要在另外一个线程中执行,彼得的目的方法完成的通知也是一样的道理。但是,迈克和彼得是好朋友,他很熟悉线程的事情,可以在这个领域提供指导。

 

他们永远幸福地生活下去……<完>





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值