《丁丁历险记系列之委托》-- 摘自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 }

 

posted @ 2010-07-26 22:46  liming1019  阅读(1073)  评论(0编辑  收藏  举报