• 《丁丁历险记系列之委托》 摘自http://www.cnblogs.com/xfxxx/archive/2010/04/03/1703839.html


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

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

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

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

    1 using System;
    2
    3  class Market
    4 {
    5
    6 #region 故事开始
    7
    8 //从前,在南方一块奇异的土地上,有个程序员名叫丁丁,他在一家知名的软件公司M公司负责产品的市场促销工作。
    9 //他非常勤奋,对他的老板总是百依百顺,但是他的老板从不信任别人,坚决要求随时知道丁丁的工作进度,以防止他偷懒。
    10 //丁丁是个喜欢思考的人,但是由于原先没有经验,他只能自己摸索着一步一步的找到了完美的解决方案。
    11 //下面,让我们跟随着丁丁的足迹,探寻他是怎样成长和进步的。
    12  
    13 static void Main()
    14 {
    15 //您只需要修改下面的参数1-9,然后编译运行即可看到不同的运行结果
    16   ExecuteStep(1);
    17 //或者使用这个循环,遍历所有结果
    18 //for (int i = 1; i < 10; i++)
    19 //ExecuteStep(i);
    20   }
    21
    22 static void ExecuteStep(int step)
    23 {
    24 switch (step)
    25 {
    26 case 1: ED1_通知方法(); break;
    27 case 2: ED2_接口(); break;
    28 case 3: ED3_委托(); break;
    29 case 4: ED4_静态监听者(); break;
    30 case 5: ED5_事件(); break;
    31 case 6: ED6_收获所有结果(); break;
    32 case 7: ED7_异步通知_激发(); break;
    33 case 8: ED8_异步通知_轮询(); break;
    34 case 9: ED9_异步通知_回调(); break;
    35 }
    36 }
    37
    38 #endregion
    39
    40 #region 通知方法
    41
    42 //首先丁丁考虑的是怎样能不让老板呆在他的办公室里站在背后盯着他,于是就对老板做出承诺:无论何时,
    43 //只要我的工作取得了一点进展我都会及时让你知道。
    44 //丁丁通过周期性地使用“带类型的引用(typed reference)”来“回调”他的老板来实现他的承诺:
    45  
    46 #region Boss1类
    47 class Boss1
    48 {
    49 public void WorkStarted() { /* 老板不关心。*/ }
    50 public void WorkProgressing() { /* 老板不关心。*/ }
    51 public int WorkCompleted()
    52 {
    53 Console.WriteLine("老板评价:仍需努力!给 1 分");
    54 return 1; //总分为10
    55   }
    56 }
    57 #endregion
    58
    59 #region Worker1类
    60 class Worker1
    61 {
    62 Boss1 _boss;
    63 public void Advise(Boss1 boss)
    64 {
    65 _boss = boss;
    66 }
    67
    68 public void DoWork()
    69 {
    70 Console.WriteLine("丁丁:工作开始");
    71 if (_boss != null)
    72 _boss.WorkStarted();
    73
    74 Console.WriteLine("丁丁:工作进行中");
    75 if (_boss != null)
    76 _boss.WorkProgressing();
    77
    78 Console.WriteLine("丁丁:工作完成!自我打分: 3 分");
    79 if (_boss != null)
    80 {
    81 int grade = _boss.WorkCompleted();
    82 Console.WriteLine("丁丁的工作得分=" + grade);
    83 }
    84 }
    85 }
    86 #endregion
    87
    88 private static void ED1_通知方法()
    89 {
    90 Console.WriteLine("ED1_通知方法---------------------------------------");
    91 Worker1 dingding = new Worker1();
    92 Boss1 boss = new Boss1();
    93 dingding.Advise(boss);
    94 dingding.DoWork();
    95 Console.WriteLine("公司消息:产品促销工作顺利结束!");
    96 Console.ReadLine();
    97 }
    98
    99 #endregion
    100
    101 #region 接口
    102
    103 //现在,丁丁成了一个特殊的人,他不但能容忍吝啬的老板,而且和他周围的市场中的客户也有了密切的联系,
    104 //以至于他认为市场中所有客户对他的工作进度也感兴趣。不幸的是,他必须也给市场添加一个特殊的回调函数Advise
    105 //来实现同时向他老板和市场报告工作进度。丁丁想要把潜在的通知的列表和这些通知的实现方法分离开来,
    106 //于是他决定把方法分离为一个接口:IWorkerEvents
    107  
    108 #region IWorkerEvents接口
    109 public interface IWorkerEvents
    110 {
    111 void WorkStarted();
    112 void WorkProgressing();
    113 int WorkCompleted();
    114 }
    115 #endregion
    116
    117 #region Boss2类
    118 class Boss2 : IWorkerEvents
    119 {
    120 public void WorkStarted() { /* 老板不关心。*/ }
    121 public void WorkProgressing() { /* 老板不关心。*/ }
    122 public int WorkCompleted()
    123 {
    124 Console.WriteLine("老板评价:还可以!给 4 分");
    125 return 4;
    126 }
    127 }
    128 #endregion
    129
    130 #region Worker2类
    131 class Worker2
    132 {
    133 public void Advise(IWorkerEvents events)
    134 {
    135 _events = events;
    136 }
    137
    138 public void DoWork()
    139 {
    140 Console.WriteLine("丁丁:工作开始");
    141 if (_events != null)
    142 _events.WorkStarted();
    143 Console.WriteLine("丁丁:工作进行中");
    144 if (_events != null)
    145 _events.WorkProgressing();
    146 Console.WriteLine("丁丁:工作完成!自我打分: 4 分");
    147 if (_events != null)
    148 {
    149 int grade = _events.WorkCompleted();
    150 Console.WriteLine("丁丁的工作得分=" + grade);
    151 }
    152 }
    153 private IWorkerEvents _events;
    154 }
    155 #endregion
    156
    157 private static void ED2_接口()
    158 {
    159 Console.WriteLine("ED2_接口---------------------------------------");
    160 Worker2 dingding = new Worker2();
    161 Boss2 boss = new Boss2();
    162 dingding.Advise(boss);
    163 dingding.DoWork();
    164 Console.WriteLine("公司消息:产品促销工作顺利结束!");
    165 Console.ReadLine();
    166 }
    167
    168 #endregion
    169
    170 #region 委托
    171
    172 //不幸的是,这没有解决问题。每当丁丁忙于通过接口的实现和老板交流时,就没有机会及时通知市场了。
    173 //至少他不能忽略身在远方的老板的引用,以此来让市场中的其他实现了IWorkerEvents的客户得到他的工作报告。
    174 //他的老板还是抱怨得很厉害。“丁丁!”他老板吼道,“你为什么在工作一开始和工作进行中都来烦我?!
    175 //我不关心这些事件。你不但强迫我实现了这些方法,而且还在浪费我宝贵的工作时间来处理你的事件,
    176 //特别是当我外出的时候更是如此!你能不能不再来烦我?”
    177 //于是,丁丁意识到接口虽然在很多情况都很有用,但是当用作事件时,“效果”不够好。
    178 //他希望能够仅在别人想要时才通知他们,于是他决定把接口的方法分离为单独的委托,
    179 //每个委托都像一个小的接口方法:
    180
    181 #region 委托类型的定义
    182 public delegate void WorkStarted();
    183 public delegate void WorkProgressing();
    184 public delegate int WorkCompleted();
    185 #endregion
    186
    187 #region Boss3类
    188 class Boss3
    189 {
    190 public int WorkCompleted()
    191 {
    192 Console.WriteLine("老板评价:很好!给 7 分");
    193 return 7;
    194 }
    195 }
    196 #endregion
    197
    198 #region Worker3类
    199 class Worker3
    200 {
    201 public WorkStarted started;
    202 public WorkProgressing progressing;
    203 public WorkCompleted completed;
    204
    205 public void DoWork()
    206 {
    207 Console.WriteLine("丁丁:工作开始");
    208 if (started != null)
    209 started();
    210 Console.WriteLine("丁丁:工作进行中");
    211 if (progressing != null)
    212 progressing();
    213 Console.WriteLine("丁丁:工作完成!自我打分: 5 分");
    214 if (completed != null)
    215 {
    216 int grade = completed();
    217 Console.WriteLine("丁丁的工作得分=" + grade);
    218 }
    219 }
    220 }
    221 #endregion
    222
    223 private static void ED3_委托()
    224 {
    225 Console.WriteLine("ED3_委托---------------------------------------");
    226 Worker3 dingding = new Worker3();
    227 Boss3 boss = new Boss3();
    228 dingding.completed = new WorkCompleted(boss.WorkCompleted);
    229 dingding.DoWork();
    230 Console.WriteLine("公司消息:产品促销工作顺利结束!");
    231 Console.ReadLine();
    232 }
    233
    234 #endregion
    235
    236 #region 静态监听者
    237
    238 //这样,丁丁不会再拿他老板不想要的事件来烦他老板了,但是他还没有把市场放到他的监听者列表中。
    239 //因为市场是个包涵一切的实体,看来不适合使用实例方法的委托(想像一下,实例化一个市场中的所有客户要花费多少资源!)
    240 //于是丁丁就需要能够对静态委托进行挂钩,委托对这一点支持得很好
    241
    242 static void WorkerStartedWork1()
    243 {
    244 Console.WriteLine("市场知道M公司已经开始产品促销了!");
    245 }
    246
    247 static int WorkerCompletedWork1()
    248 {
    249 Console.WriteLine("市场很满意M公司的产品促销活动!给 5 分");
    250 return 5;
    251 }
    252
    253 private static void ED4_静态监听者()
    254 {
    255 Console.WriteLine("ED4_静态监听者---------------------------------------");
    256 Worker3 dingding = new Worker3();
    257 Boss3 boss = new Boss3();
    258 dingding.completed += new WorkCompleted(boss.WorkCompleted);
    259 dingding.started += new WorkStarted(Market.WorkerStartedWork1);
    260 dingding.completed += new WorkCompleted(Market.WorkerCompletedWork1);
    261 dingding.DoWork();
    262 Console.WriteLine("公司消息:产品促销工作顺利结束!");
    263 Console.ReadLine();
    264 }
    265
    266 #endregion
    267
    268 #region 事件
    269
    270 //不幸的是,市场太繁忙了,也不习惯时刻关注它里面的个体,它可以用自己的委托替换了丁丁老板的委托。
    271 //这是把丁丁的Worker类的的委托字段做成public的一个无意识的副作用。
    272 //同样,如果丁丁的老板不耐烦了,也可以决定自己来激发丁丁的委托(真是一个粗鲁的老板):
    273 //丁丁的老板可以使用下面的方法来亲手强制其完成工作
    274 //if(dingding.completed != null) dingding.completed();
    275
    276 //丁丁不想让这些事发生,他意识到需要给每个委托提供“注册”和“反注册”的功能,
    277 //这样监听者就可以自己添加和移除委托,但同时又不能清空整个列表也不能随意激发丁丁的事件了。
    278 //丁丁并没有来自己实现这些功能,相反,他使用了event关键字让C#编译器为他构建这些方法:
    279
    280 //丁丁知道event关键字在委托的外边包装了一个Property,仅让客户通过+=和-=操作符来添加和移除,
    281 //强迫他的老板和市场正确地使用事件。
    282
    283 #region Worker4类
    284 class Worker4
    285 {
    286 public event WorkStarted started;
    287 public event WorkProgressing progressing;
    288 public event WorkCompleted completed;
    289
    290 public void DoWork()
    291 {
    292 Console.WriteLine("丁丁:工作开始");
    293 if (started != null) started();
    294 Console.WriteLine("丁丁:工作进行中");
    295 if (progressing != null) progressing();
    296 Console.WriteLine("丁丁:工作完成!自我打分: 6 分");
    297 if (completed != null)
    298 {
    299 int grade = completed();
    300 Console.WriteLine("丁丁的工作得分=" + grade);
    301 }
    302 }
    303 }
    304 #endregion
    305
    306 private static void ED5_事件()
    307 {
    308 Console.WriteLine("ED5_事件---------------------------------------");
    309 Worker4 dingding = new Worker4();
    310 Boss3 boss = new Boss3();
    311 dingding.completed += new WorkCompleted(boss.WorkCompleted);
    312 dingding.started += new WorkStarted(Market.WorkerStartedWork1);
    313 dingding.completed += new WorkCompleted(Market.WorkerCompletedWork1);
    314 dingding.DoWork();
    315 Console.WriteLine("公司消息:产品促销工作顺利结束!");
    316 Console.ReadLine();
    317 }
    318
    319 #endregion
    320
    321 #region "收获"所有结果
    322
    323 //到这时,丁丁终于可以送一口气了,他成功地满足了所有监听者的需求,同时避免了与特定实现的紧耦合。
    324 //但是他注意到他的老板和市场都为它的工作打了分,但是他仅仅接收了一个分数。
    325 //面对多个监听者,他想要"收获"所有的结果,于是他深入到代理里面,轮询监听者列表,手工一个个调用:
    326
    327 #region Worker5类
    328 class Worker5
    329 {
    330 public event WorkStarted started;
    331 public event WorkProgressing progressing;
    332 public event WorkCompleted completed;
    333
    334 public void DoWork()
    335 {
    336 Console.WriteLine("丁丁:工作开始");
    337 if (started != null) started();
    338 Console.WriteLine("丁丁:工作进行中");
    339 if (progressing != null) progressing();
    340 Console.WriteLine("丁丁:工作完成!自我打分: 7 分");
    341 if (completed != null)
    342 {
    343 //遍历代理中的所有委托对象,依次获取结果
    344 foreach (WorkCompleted wc in completed.GetInvocationList())
    345 {
    346 int grade = wc();
    347 Console.WriteLine("丁丁的工作得分=" + grade);
    348 }
    349 }
    350 }
    351 }
    352 #endregion
    353
    354 private static void ED6_收获所有结果()
    355 {
    356 Console.WriteLine("ED6_收获所有结果---------------------------------------");
    357 Worker5 dingding = new Worker5();
    358 Boss3 boss = new Boss3();
    359 dingding.completed += new WorkCompleted(boss.WorkCompleted);
    360 dingding.started += new WorkStarted(Market.WorkerStartedWork1);
    361 dingding.completed += new WorkCompleted(Market.WorkerCompletedWork1);
    362 dingding.DoWork();
    363 Console.WriteLine("公司消息:产品促销工作顺利结束!");
    364 Console.ReadLine();
    365 }
    366
    367 #endregion
    368
    369 #region 异步通知:激发
    370
    371 //同时,他的老板和市场还要忙于处理其他事情,也就是说他们给丁丁打分所花费的事件变得非常长:
    372 //很不幸,丁丁每次通知一个监听者后必须等待它给自己打分,现在这些通知花费了他太多的工作时间。
    373 //于是他决定忘掉分数,仅仅异步激发事件:
    374
    375 #region Boss4类
    376 class Boss4
    377 {
    378 public int WorkCompleted()
    379 {
    380 System.Threading.Thread.Sleep(3000);
    381 Console.WriteLine("老板评价:非常好!给 10 分");
    382 return 10;
    383 }
    384 }
    385 #endregion
    386
    387 #region Worker6类
    388 class Worker6
    389 {
    390 public event WorkStarted started;
    391 public event WorkProgressing progressing;
    392 public event WorkCompleted completed;
    393
    394 public void DoWork()
    395 {
    396 Console.WriteLine("丁丁:工作开始");
    397 if (started != null) started();
    398 Console.WriteLine("丁丁:工作进行中");
    399 if (progressing != null) progressing();
    400 Console.WriteLine("丁丁:工作完成!自我打分: 8 分");
    401 if (completed != null)
    402 {
    403 foreach (WorkCompleted wc in completed.GetInvocationList())
    404 {
    405 wc.BeginInvoke(null, null);
    406 }
    407 }
    408 }
    409 }
    410 #endregion
    411
    412 static void WorkerStartedWork2()
    413 {
    414 Console.WriteLine("市场知道M公司已经开始产品促销了!");
    415 }
    416
    417 static int WorkerCompletedWork2()
    418 {
    419 //暂停进程一段时间以模拟繁忙程度
    420 System.Threading.Thread.Sleep(4000);
    421 Console.WriteLine("市场很满意M公司的产品促销活动!给 10 分");
    422 return 10;
    423 }
    424
    425 private static void ED7_异步通知_激发()
    426 {
    427 Console.WriteLine("ED7_异步通知_激发---------------------------------------");
    428 Worker6 dingding = new Worker6();
    429 Boss4 boss = new Boss4();
    430 dingding.completed += new WorkCompleted(boss.WorkCompleted);
    431 dingding.started += new WorkStarted(Market.WorkerStartedWork2);
    432 dingding.completed += new WorkCompleted(Market.WorkerCompletedWork2);
    433 dingding.DoWork();
    434 Console.WriteLine("公司消息:产品促销工作顺利结束!");
    435 Console.ReadLine();
    436 }
    437
    438 #endregion
    439
    440 #region 异步通知:轮询
    441
    442 //这使得丁丁可以通知他的监听者,然后立即返回工作,让进程的线程池来调用这些代理。随着时间的过去,
    443 //丁丁发现他丢失了他工作的反馈,他知道听取别人的赞扬和努力工作一样重要,于是他不但异步激发事件,
    444 //还要周期性地轮询,取得可用的分数。
    445
    446 #region Worker7类
    447 class Worker7
    448 {
    449 public event WorkStarted started;
    450 public event WorkProgressing progressing;
    451 public event WorkCompleted completed;
    452
    453 public void DoWork()
    454 {
    455 Console.WriteLine("丁丁:工作开始");
    456 if (started != null) started();
    457 Console.WriteLine("丁丁:工作进行中");
    458 if (progressing != null) progressing();
    459 Console.WriteLine("丁丁:工作完成!自我打分: 9 分");
    460 if (completed != null)
    461 {
    462 foreach (WorkCompleted wc in completed.GetInvocationList())
    463 {
    464 IAsyncResult res = wc.BeginInvoke(null, null);
    465 while (!res.IsCompleted)
    466 System.Threading.Thread.Sleep(1);
    467 int grade = wc.EndInvoke(res);
    468 Console.WriteLine("丁丁的工作得分=" + grade);
    469 }
    470 }
    471 }
    472 }
    473 #endregion
    474
    475 private static void ED8_异步通知_轮询()
    476 {
    477 Console.WriteLine("ED8_异步通知_轮询---------------------------------------");
    478 Worker7 dingding = new Worker7();
    479 Boss4 boss = new Boss4();
    480 dingding.completed += new WorkCompleted(boss.WorkCompleted);
    481 dingding.started += new WorkStarted(Market.WorkerStartedWork2);
    482 dingding.completed += new WorkCompleted(Market.WorkerCompletedWork2);
    483 dingding.DoWork();
    484 Console.WriteLine("公司消息:产品促销工作顺利结束!");
    485 Console.ReadLine();
    486 }
    487
    488 #endregion
    489
    490 #region 异步通知:回调
    491
    492 //不幸地,丁丁有回到了一开始就想避免的情况中来,比如,老板站在背后盯着他工作。
    493 //于是,他决定使用自己的委托回调函数作为他调用的异步委托完成的通知,让他自己立即回到工作,
    494 //但是仍可以在别人给他的工作打分后得到通知:
    495
    496 #region Worker8类
    497 class Worker8
    498 {
    499 public event WorkStarted started;
    500 public event WorkProgressing progressing;
    501 public event WorkCompleted completed;
    502
    503 public void DoWork()
    504 {
    505 Console.WriteLine("丁丁:工作开始");
    506 if (started != null) started();
    507 Console.WriteLine("丁丁:工作进行中");
    508 if (progressing != null) progressing();
    509 Console.WriteLine("丁丁:工作完成!自我打分: 10 分");
    510 if (completed != null)
    511 {
    512 foreach (WorkCompleted wc in completed.GetInvocationList())
    513 {
    514 wc.BeginInvoke(new AsyncCallback(WorkGraded), wc);
    515 }
    516 }
    517 }
    518
    519 private void WorkGraded(IAsyncResult res)
    520 {
    521 WorkCompleted wc = (WorkCompleted)res.AsyncState;
    522 int grade = wc.EndInvoke(res);
    523 Console.WriteLine("丁丁的工作得分=" + grade);
    524 }
    525 }
    526 #endregion
    527
    528 private static void ED9_异步通知_回调()
    529 {
    530 Console.WriteLine("ED9_异步通知_回调---------------------------------------");
    531 Worker8 dingding = new Worker8();
    532 Boss4 boss = new Boss4();
    533 dingding.completed += new WorkCompleted(boss.WorkCompleted);
    534 dingding.started += new WorkStarted(Market.WorkerStartedWork2);
    535 dingding.completed += new WorkCompleted(Market.WorkerCompletedWork2);
    536 dingding.DoWork();
    537 Console.WriteLine("公司消息:产品促销工作顺利结束!");
    538 Console.ReadLine();
    539 }
    540
    541 #endregion
    542
    543 #region 尾声
    544
    545 //整个软件市场的繁荣
    546 //丁丁、他的老板和市场最终都满足了。丁丁的老板和市场可以收到他们感兴趣的事件通知,
    547 //减少了实现的负担和非必需的往返“差旅费”。丁丁可以通知他们,而不管他们要花多长时间来从目的方法中返回,
    548 //同时又可以异步地得到他的结果。丁丁知道,这并不简单,因为当他异步激发事件时,
    549 //方法要在另外一个线程中执行,丁丁的目的方法完成的通知也是一样的道理。
    550 //于是丁丁便开始着手研究线程了……<本章完>
    551
    552 #endregion
    553
    554 }
  • 相关阅读:
    Redis 设计与实现 2:Redis 对象 redisObject
    Redis 设计与实现 1:数据库 redisDb
    KafkaProducer 简析
    G1 收集器
    KafkaMirrorMaker 的不足以及一些改进
    Redis 数据结构与对象编码 (Object Encoding)
    跨境 TCP 传输优化实录 — 使用 BBR 解决 LFN 问题
    TCP 协议简析
    使用模拟退火算法优化 Hash 函数
    LSM-Tree 与 B-Tree
  • 原文地址:https://www.cnblogs.com/liming1019/p/1785636.html
Copyright © 2020-2023  润新知