本文根据《.NET委托:一个C#睡前故事》改编,我的初衷是通过一个类似于小说的模式,使用C#语言为背景,将编程的基础知识以一种很容易理解的方式展现给初学者。

虽然我还有日常的工作要做,其中包括C#的培训工作(本文也是我曾经用于培训学生时一堂课的内容),但我会尽量抽时间,争取陆续的推出该系列的其它姊妹篇。

下面的代码使用起来非常简单的,直接粘贴到命令行项目中的Program中即可运行,运行有两种模式,一种是单步运行,修改 ExecuteStep(1);中的参数1-9

另一种是注释该语句,取消注释下面的for循环两行,编译运行即可。而且,给读者的感觉看代码就像看小说一样有趣!

using System;

class Market
{

#region 故事开始

//从前,在南方一块奇异的土地上,有个程序员名叫丁丁,他在一家知名的软件公司M公司负责产品的市场促销工作。
  //他非常勤奋,对他的老板总是百依百顺,但是他的老板从不信任别人,坚决要求随时知道丁丁的工作进度,以防止他偷懒。
  //丁丁是个喜欢思考的人,但是由于原先没有经验,他只能自己摸索着一步一步的找到了完美的解决方案。
  //下面,让我们跟随着丁丁的足迹,探寻他是怎样成长和进步的。

static void Main()
  {
    //您只需要修改下面的参数1-9,然后编译运行即可看到不同的运行结果
    ExecuteStep(1);
    //或者使用这个循环,遍历所有结果
    //for (int i = 1; i < 10; i++)
      //ExecuteStep(i);
  }

static void ExecuteStep(int step)
  {
    switch (step)
    {
      case 1: ED1_通知方法(); break;
      case 2: ED2_接口(); break;
      case 3: ED3_委托(); break;
      case 4: ED4_静态监听者(); break;
      case 5: ED5_事件(); break;
      case 6: ED6_收获所有结果(); break;
      case 7: ED7_异步通知_激发(); break;
      case 8: ED8_异步通知_轮询(); break;
      case 9: ED9_异步通知_回调(); break;
    }
  }

#endregion

#region 通知方法

//首先丁丁考虑的是怎样能不让老板呆在他的办公室里站在背后盯着他,于是就对老板做出承诺:无论何时,
  //只要我的工作取得了一点进展我都会及时让你知道。
  //丁丁通过周期性地使用“带类型的引用(typed reference)”来“回调”他的老板来实现他的承诺:

#region Boss1类
  class Boss1
  {
    public void WorkStarted() {        /* 老板不关心。*/      }
    public void WorkProgressing() {        /* 老板不关心。*/      }
    public int WorkCompleted()
    {
      Console.WriteLine("老板评价:仍需努力!给 1 分");
      return 1; //总分为10
    }
  }
  #endregion

#region Worker1类
  class Worker1
  {
    Boss1 _boss;
    public void Advise(Boss1 boss)
    {
      _boss = boss;
    }

public void DoWork()
    {
      Console.WriteLine("丁丁:工作开始");
      if (_boss != null)
        _boss.WorkStarted();

Console.WriteLine("丁丁:工作进行中");
      if (_boss != null)
        _boss.WorkProgressing();

Console.WriteLine("丁丁:工作完成!自我打分: 3 分");
      if (_boss != null)
      {
        int grade = _boss.WorkCompleted();
        Console.WriteLine("丁丁的工作得分=" + grade);
      }
    }
  }
  #endregion

private static void ED1_通知方法()
  {
    Console.WriteLine("ED1_通知方法---------------------------------------");
    Worker1 dingding = new Worker1();
    Boss1 boss = new Boss1();
    dingding.Advise(boss);
    dingding.DoWork();
    Console.WriteLine("公司消息:产品促销工作顺利结束!");
    Console.ReadLine();
  }

#endregion

#region 接口

//现在,丁丁成了一个特殊的人,他不但能容忍吝啬的老板,而且和他周围的市场中的客户也有了密切的联系,
  //以至于他认为市场中所有客户对他的工作进度也感兴趣。不幸的是,他必须也给市场添加一个特殊的回调函数Advise
  //来实现同时向他老板和市场报告工作进度。丁丁想要把潜在的通知的列表和这些通知的实现方法分离开来,
  //于是他决定把方法分离为一个接口:IWorkerEvents

#region IWorkerEvents接口
  public interface IWorkerEvents
  {
    void WorkStarted();
    void WorkProgressing();
    int WorkCompleted();
  }
  #endregion

#region Boss2类
  class Boss2 : IWorkerEvents
  {
    public void WorkStarted() {        /* 老板不关心。*/      }
    public void WorkProgressing() {        /* 老板不关心。*/      }
    public int WorkCompleted()
    {
      Console.WriteLine("老板评价:还可以!给 4 分");
      return 4;
    }
  }
  #endregion

#region Worker2类
  class Worker2
  {
    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("丁丁:工作完成!自我打分: 4 分");
      if (_events != null)
      {
        int grade = _events.WorkCompleted();
        Console.WriteLine("丁丁的工作得分=" + grade);
      }
    }
    private IWorkerEvents _events;
  }
  #endregion

private static void ED2_接口()
  {
    Console.WriteLine("ED2_接口---------------------------------------");
    Worker2 dingding = new Worker2();
    Boss2 boss = new Boss2();
    dingding.Advise(boss);
    dingding.DoWork();
    Console.WriteLine("公司消息:产品促销工作顺利结束!");
    Console.ReadLine();
  }

#endregion

#region 委托

//不幸的是,这没有解决问题。每当丁丁忙于通过接口的实现和老板交流时,就没有机会及时通知市场了。
  //至少他不能忽略身在远方的老板的引用,以此来让市场中的其他实现了IWorkerEvents的客户得到他的工作报告。
  //他的老板还是抱怨得很厉害。“丁丁!”他老板吼道,“你为什么在工作一开始和工作进行中都来烦我?!
  //我不关心这些事件。你不但强迫我实现了这些方法,而且还在浪费我宝贵的工作时间来处理你的事件,
  //特别是当我外出的时候更是如此!你能不能不再来烦我?”
  //于是,丁丁意识到接口虽然在很多情况都很有用,但是当用作事件时,“效果”不够好。
  //他希望能够仅在别人想要时才通知他们,于是他决定把接口的方法分离为单独的委托,
  //每个委托都像一个小的接口方法:

#region 委托类型的定义
  public delegate void WorkStarted();
  public delegate void WorkProgressing();
  public delegate int WorkCompleted();
  #endregion

#region Boss3类
  class Boss3
  {
    public int WorkCompleted()
    {
      Console.WriteLine("老板评价:很好!给 7 分");
      return 7;
    }
  }
  #endregion

#region Worker3类
  class Worker3
  {
    public WorkStarted started;
    public WorkProgressing progressing;
    public WorkCompleted completed;

public void DoWork()
    {
      Console.WriteLine("丁丁:工作开始");
      if (started != null)
        started();
      Console.WriteLine("丁丁:工作进行中");
      if (progressing != null)
        progressing();
      Console.WriteLine("丁丁:工作完成!自我打分: 5 分");
      if (completed != null)
      {
        int grade = completed();
        Console.WriteLine("丁丁的工作得分=" + grade);
      }
    }
  }
  #endregion

private static void ED3_委托()
  {
    Console.WriteLine("ED3_委托---------------------------------------");
    Worker3 dingding = new Worker3();
    Boss3 boss = new Boss3();
    dingding.completed = new WorkCompleted(boss.WorkCompleted);
    dingding.DoWork();
    Console.WriteLine("公司消息:产品促销工作顺利结束!");
    Console.ReadLine();
  }

#endregion

#region 静态监听者

//这样,丁丁不会再拿他老板不想要的事件来烦他老板了,但是他还没有把市场放到他的监听者列表中。
  //因为市场是个包涵一切的实体,看来不适合使用实例方法的委托(想像一下,实例化一个市场中的所有客户要花费多少资源!)
  //于是丁丁就需要能够对静态委托进行挂钩,委托对这一点支持得很好

static void WorkerStartedWork1()
  {
    Console.WriteLine("市场知道M公司已经开始产品促销了!");
  }

static int WorkerCompletedWork1()
  {
    Console.WriteLine("市场很满意M公司的产品促销活动!给 5 分");
    return 5;
  }

private static void ED4_静态监听者()
  {
    Console.WriteLine("ED4_静态监听者---------------------------------------");
    Worker3 dingding = new Worker3();
    Boss3 boss = new Boss3();
    dingding.completed += new WorkCompleted(boss.WorkCompleted);
    dingding.started += new WorkStarted(Market.WorkerStartedWork1);
    dingding.completed += new WorkCompleted(Market.WorkerCompletedWork1);
    dingding.DoWork();
    Console.WriteLine("公司消息:产品促销工作顺利结束!");
    Console.ReadLine();
  }

#endregion

#region 事件

//不幸的是,市场太繁忙了,也不习惯时刻关注它里面的个体,它可以用自己的委托替换了丁丁老板的委托。
  //这是把丁丁的Worker类的的委托字段做成public的一个无意识的副作用。
  //同样,如果丁丁的老板不耐烦了,也可以决定自己来激发丁丁的委托(真是一个粗鲁的老板):
  //丁丁的老板可以使用下面的方法来亲手强制其完成工作
  //if(dingding.completed != null) dingding.completed();

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

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

#region Worker4类
  class Worker4
  {
    public event WorkStarted started;
    public event WorkProgressing progressing;
    public event WorkCompleted completed;

public void DoWork()
    {
      Console.WriteLine("丁丁:工作开始");
      if (started != null) started();
      Console.WriteLine("丁丁:工作进行中");
      if (progressing != null) progressing();
      Console.WriteLine("丁丁:工作完成!自我打分: 6 分");
      if (completed != null)
      {
        int grade = completed();
        Console.WriteLine("丁丁的工作得分=" + grade);
      }
    }
  }
  #endregion

private static void ED5_事件()
  {
    Console.WriteLine("ED5_事件---------------------------------------");
    Worker4 dingding = new Worker4();
    Boss3 boss = new Boss3();
    dingding.completed += new WorkCompleted(boss.WorkCompleted);
    dingding.started += new WorkStarted(Market.WorkerStartedWork1);
    dingding.completed += new WorkCompleted(Market.WorkerCompletedWork1);
    dingding.DoWork();
    Console.WriteLine("公司消息:产品促销工作顺利结束!");
    Console.ReadLine();
  }

#endregion

#region "收获"所有结果

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

#region Worker5类
  class Worker5
  {
    public event WorkStarted started;
    public event WorkProgressing progressing;
    public event WorkCompleted completed;

public void DoWork()
    {
      Console.WriteLine("丁丁:工作开始");
      if (started != null) started();
      Console.WriteLine("丁丁:工作进行中");
      if (progressing != null) progressing();
      Console.WriteLine("丁丁:工作完成!自我打分: 7 分");
      if (completed != null)
      {
        //遍历代理中的所有委托对象,依次获取结果
        foreach (WorkCompleted wc in completed.GetInvocationList())
        {
          int grade = wc();
          Console.WriteLine("丁丁的工作得分=" + grade);
        }
      }
    }
  }
  #endregion

private static void ED6_收获所有结果()
  {
    Console.WriteLine("ED6_收获所有结果---------------------------------------");
    Worker5 dingding = new Worker5();
    Boss3 boss = new Boss3();
    dingding.completed += new WorkCompleted(boss.WorkCompleted);
    dingding.started += new WorkStarted(Market.WorkerStartedWork1);
    dingding.completed += new WorkCompleted(Market.WorkerCompletedWork1);
    dingding.DoWork();
    Console.WriteLine("公司消息:产品促销工作顺利结束!");
    Console.ReadLine();
  }

#endregion

#region 异步通知:激发

//同时,他的老板和市场还要忙于处理其他事情,也就是说他们给丁丁打分所花费的事件变得非常长:
  //很不幸,丁丁每次通知一个监听者后必须等待它给自己打分,现在这些通知花费了他太多的工作时间。
  //于是他决定忘掉分数,仅仅异步激发事件:

#region Boss4类
  class Boss4
  {
    public int WorkCompleted()
    {
      System.Threading.Thread.Sleep(3000);
      Console.WriteLine("老板评价:非常好!给 10 分");
      return 10;
    }
  }
  #endregion

#region Worker6类
  class Worker6
  {
    public event WorkStarted started;
    public event WorkProgressing progressing;
    public event WorkCompleted completed;

public void DoWork()
    {
      Console.WriteLine("丁丁:工作开始");
      if (started != null) started();
      Console.WriteLine("丁丁:工作进行中");
      if (progressing != null) progressing();
      Console.WriteLine("丁丁:工作完成!自我打分: 8 分");
      if (completed != null)
      {
        foreach (WorkCompleted wc in completed.GetInvocationList())
        {
          wc.BeginInvoke(null, null);
        }
      }
    }
  }
  #endregion

static void WorkerStartedWork2()
  {
    Console.WriteLine("市场知道M公司已经开始产品促销了!");
  }

static int WorkerCompletedWork2()
  {
    //暂停进程一段时间以模拟繁忙程度
    System.Threading.Thread.Sleep(4000);
    Console.WriteLine("市场很满意M公司的产品促销活动!给 10 分");
    return 10;
  }

private static void ED7_异步通知_激发()
  {
    Console.WriteLine("ED7_异步通知_激发---------------------------------------");
    Worker6 dingding = new Worker6();
    Boss4 boss = new Boss4();
    dingding.completed += new WorkCompleted(boss.WorkCompleted);
    dingding.started += new WorkStarted(Market.WorkerStartedWork2);
    dingding.completed += new WorkCompleted(Market.WorkerCompletedWork2);
    dingding.DoWork();
    Console.WriteLine("公司消息:产品促销工作顺利结束!");
    Console.ReadLine();
  }

#endregion

#region 异步通知:轮询

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

#region Worker7类
  class Worker7
  {
    public event WorkStarted started;
    public event WorkProgressing progressing;
    public event WorkCompleted completed;

public void DoWork()
    {
      Console.WriteLine("丁丁:工作开始");
      if (started != null) started();
      Console.WriteLine("丁丁:工作进行中");
      if (progressing != null) progressing();
      Console.WriteLine("丁丁:工作完成!自我打分: 9 分");
      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);
        }
      }
    }
  }
  #endregion

private static void ED8_异步通知_轮询()
  {
    Console.WriteLine("ED8_异步通知_轮询---------------------------------------");
    Worker7 dingding = new Worker7();
    Boss4 boss = new Boss4();
    dingding.completed += new WorkCompleted(boss.WorkCompleted);
    dingding.started += new WorkStarted(Market.WorkerStartedWork2);
    dingding.completed += new WorkCompleted(Market.WorkerCompletedWork2);
    dingding.DoWork();
    Console.WriteLine("公司消息:产品促销工作顺利结束!");
    Console.ReadLine();
  }

#endregion

#region 异步通知:回调

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

#region Worker8类
  class Worker8
  {
    public event WorkStarted started;
    public event WorkProgressing progressing;
    public event WorkCompleted completed;

public void DoWork()
    {
      Console.WriteLine("丁丁:工作开始");
      if (started != null) started();
      Console.WriteLine("丁丁:工作进行中");
      if (progressing != null) progressing();
      Console.WriteLine("丁丁:工作完成!自我打分: 10 分");
      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);
    }
  }
  #endregion

private static void ED9_异步通知_回调()
  {
    Console.WriteLine("ED9_异步通知_回调---------------------------------------");
    Worker8 dingding = new Worker8();
    Boss4 boss = new Boss4();
    dingding.completed += new WorkCompleted(boss.WorkCompleted);
    dingding.started += new WorkStarted(Market.WorkerStartedWork2);
    dingding.completed += new WorkCompleted(Market.WorkerCompletedWork2);
    dingding.DoWork();
    Console.WriteLine("公司消息:产品促销工作顺利结束!");
    Console.ReadLine();
  }

#endregion

#region 尾声

//整个软件市场的繁荣
  //丁丁、他的老板和市场最终都满足了。丁丁的老板和市场可以收到他们感兴趣的事件通知,
  //减少了实现的负担和非必需的往返“差旅费”。丁丁可以通知他们,而不管他们要花多长时间来从目的方法中返回,
  //同时又可以异步地得到他的结果。丁丁知道,这并不简单,因为当他异步激发事件时,
  //方法要在另外一个线程中执行,丁丁的目的方法完成的通知也是一样的道理。
  //于是丁丁便开始着手研究线程了……<本章完>

#endregion

}

英文版原作者:Chris Sells(www.sellsbrothers.com)
翻译:袁晓辉(www.farproc.com http://blog.csdn.net/uoyevoli)
参考博客:http://blog.csdn.net/uoyevoli/archive/2005/09/02/469963.aspx

转载于:https://www.cnblogs.com/xfxxx/archive/2010/04/03/1703839.html

《丁丁历险记系列之委托》改编自《.NET委托:一个C#睡前故事》相关推荐

  1. 链方法[C# 基础知识系列]专题三:如何用委托包装多个方法——委托链

    最近研究链方法,稍微总结一下,以后继续补充: 弁言: 上一专题分析了下编译器是如何来翻译委托的,从中间语言的角度去看委托,希望可以帮助大家进一步的理解委托,然而之前的分析都是委托只是封装一个方法,那委 ...

  2. 【DOM系列】你真的理解事件委托(事件代理)吗?

    目录 1. 基本概念 1.1 原理 2. 事件冒泡和事件捕获 代码演示 3. addEventListener的第三个参数 4. 事件委托阶段案例 4.1 事件冒泡案例 4.2 事件捕获案例 5. 经 ...

  3. JavaScript系列—简述JS中的事件委托和事件代理

    JS中的事件委托和事件代理 什么是事件委托? 事件委托还有一个名字叫事件代理,JS高程上讲:事件委托就是利用事件冒泡,只制定一个时间处理程序,就可以管理某一类型的所有事件.我用取快递来解释这个现象: ...

  4. 第九节:委托和事件(1)(委托的发展历史、插件式编程、多播委托)

    一. 委托的发展历史和基本用法 说起委托,每个人可能都会对他有不同的理解,结合实战中委托的使用,我对其理解是:委托和类一样,是用户的一个自定义类型,委托可以有参数.有返回值,委托的关键字是delega ...

  5. 【转发】什么时候该用委托,为什么要用委托,委托有什么好处

    好多人一直在问:什么时候该用委托,为什么要用委托,委托有什么好处.... 看完下面的文章你将茅塞顿开..(看不懂的直接TDDTDS) 概念虽然我不喜欢讲太多 我们直接先来YY 个场景:我很喜欢打游戏, ...

  6. 委托、lamda表达式..委托概念-匿名函数-泛型委托-Lamda表达式-多播委托

    委托 一.什么是委托? 将一个方法作为参数传递给另一个方法(参数类型为委托delegate).   声明一个委托类型. 委托所指向的函数必须跟委托具有相同的的签名(参数个数.参数类型.返回值一样). ...

  7. C# 委托 (一)—— 委托、 泛型委托与Lambda表达式

    C# 委托 (一)-- 委托. 泛型委托与Lambda表达式 2018年08月19日 20:46:47 wnvalentin 阅读数 2992 版权声明:此文乃博主之原创.鄙人才疏,望大侠斧正.此文可 ...

  8. 第一节:复习委托,并且通过委托的异步调用开启一个新线程和异步回调、异步等待。

    一. 再谈委托 1. 委托是一个关键字为delegate的自定义类型,通过委托可以把方法以参数的形式传递给另外一个方法,实现插件式的开发模式: 同时调用委托的时候,委托所包含的所有方法都会被实现. 2 ...

  9. 第一节:复习委托,并且通过委托的异步调用开启一个新线程和异步回调、异步等待

    一. 再谈委托 1. 委托是一个关键字为delegate的自定义类型,通过委托可以把方法以参数的形式传递给另外一个方法,实现插件式的开发模式: 同时调用委托的时候,委托所包含的所有方法都会被实现. 2 ...

  10. javascript的事件冒泡,阻止事件冒泡和事件委托, 事件委托是事件冒泡的一个应用。...

    2018年12月13日更新 <!DOCTYPE html> <html lang="en"> <head><meta charset=&q ...

最新文章

  1. Windows Server 2008域中组的成员关系
  2. 9月9日项目群管理活动讨论
  3. 角谷定理python每次输出数_角谷定理C++递归问题,求问步数为什么总输出0?
  4. Cacls命令使用格式
  5. 这个只需一步就可做富集分析的网站还未发表就被CNS等引用超过350次
  6. php原生导出excel文件的两种方法
  7. python md5加密数据
  8. 远程体验Linux Lite
  9. java cache缓存_Redis缓存失效策略思考
  10. eclipse使用git合并_Eclipse的git插件冲突合并方法
  11. EXP-00091 Exporting questionable statistics
  12. 悲剧收场的梅耶尔给雅虎留下了哪些“遗产”?
  13. 交互式数据可视化_我如何构建一个交互式仪表板Web应用程序以可视化拳击数据...
  14. Android禁用系统更新
  15. openjudge666:放苹果
  16. 搜狗 2018校招 商业基础工程—测试开发练习题
  17. 【MFC】MFC中SQLite使用经验总结
  18. OPENGL三维场景搭建、漫游、交互
  19. 分享一下自己做电影解说的步骤流程和经验,小白必看!
  20. 选择公司要慎重 这样的公司运维千万别去,进了不能干太久

热门文章

  1. css 图片大小自适应div,css让图片自适应容器(div)大小
  2. 绕过密码关闭趋势防毒墙
  3. 关于绕开百度文库复制限制的那档子事
  4. ​【安全篇 / Web过滤】(5.6) ❀ 01. DNS 过滤僵尸网络 ❀ FortiGate 防火墙
  5. 巫妖易语言post-js逆向教程
  6. Linux中安装WPS
  7. 谁说中国没有 Linus?中国初代 IT 宗师封神榜
  8. ArcMap图例横放教程
  9. 最全企业级数仓建设迭代版
  10. 蚂蚁课堂视频笔记思维导图-4期 三、消息中间件