NET委托:一個關于C#的睡前故事(3)

字號:

事件 不幸的是,宇宙太忙了,也不習慣時刻關注它里面的個體,它可以用自己的委托替換了彼得老板的委托。這是把彼得的Worker類的的委托字段做成public的一個無意識的副作用。同樣,如果彼得的老板不耐煩了,也可以決定自己來激發(fā)彼得的委托(真是一個粗魯的老板):
    // Peter's boss taking matters into his own hands
    if( peter.completed != null ) peter.completed();
    彼得不想讓這些事發(fā)生,他意識到需要給每個委托提供“注冊”和“反注冊”功能,這樣監(jiān)聽者就可以自己添加和移除委托,但同時又不能清空整個列表也不能隨意激發(fā)彼得的事件了。彼得并沒有來自己實現(xiàn)這些功能,相反,他使用了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();
    }
    “收獲”所有結果
    到這時,彼得終于可以送一口氣了,他成功地滿足了所有監(jiān)聽者的需求,同時避免了與特定實現(xiàn)的緊耦合。但是他注意到他的老板和宇宙都為它的工作打了分,但是他僅僅接收了一個分數。面對多個監(jiān)聽者,他想要“收獲”所有的結果,于是他深入到代理里面,輪詢監(jiān)聽者列表,手工一個個調用:
    public void DoWork() {
    ...
    Console.WriteLine("“工作: 工作完成”");
    if( completed != null ) {
    foreach( WorkCompleted wc in completed.GetInvocationList() ) {
    int grade = wc();
    Console.WriteLine(“工人的工作得分=” + grade);
    }
    }
    }
    異步通知:激發(fā) & 忘掉
    同時,他的老板和宇宙還要忙于處理其他事情,也就是說他們給彼得打分所花費的事件變得非常長:
    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;
    }
    ...
    }
    很不幸,彼得每次通知一個監(jiān)聽者后必須等待它給自己打分,現(xiàn)在這些通知花費了他太多的工作事件。于是他決定忘掉分數,僅僅異步激發(fā)事件:
    public void DoWork() {
    ...
    Console.WriteLine("“工作: 工作完成”");
    if( completed != null ) {
    foreach( WorkCompleted wc in completed.GetInvocationList() )
    {
    wc.BeginInvoke(null, null);
    }
    }
    }
    異步通知:輪詢
    這使得彼得可以通知他的監(jiān)聽者,然后立即返回工作,讓進程的線程池來調用這些代理。隨著時間的過去,彼得發(fā)現(xiàn)他丟失了他工作的反饋,他知道聽取別人的贊揚和努力工作一樣重要,于是他異步激發(fā)事件,但是周期性地輪詢,取得可用的分數。
    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);
    }
    宇宙中的幸福
    彼得、他的老板和宇宙最終都滿足了。彼得的老板和宇宙可以收到他們感興趣的事件通知,減少了實現(xiàn)的負擔和非必需的往返“差旅費”。彼得可以通知他們,而不管他們要花多長時間來從目的方法中返回,同時又可以異步地得到他的結果。彼得知道,這并不*十分*簡單,因為當他異步激發(fā)事件時,方法要在另外一個線程中執(zhí)行,彼得的目的方法完成的通知也是一樣的道理。但是,邁克和彼得是好朋友,他很熟悉線程的事情,可以在這個領域提供指導。
    他們永遠幸福地生活下去……