您的位置:首页 > 理论基础 > 计算机网络

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

2010-07-26 22:46 671 查看
本文根据《.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

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐