一个C#睡前故事

转自:http://www.cnblogs.com/nihgwu/archive/2006/08/04/467607.html

.NET Delegates: A C# Bedtime Story

Tight Coupling

Once upon a time, in a strange land south of here, there was a worker named Peter. He was a diligent worker who would readily accept requests from his boss. However, his boss was a mean, untrusting man who insisted on steady progress reports. Since Peter did not want his boss standing in his office looking over his shoulder, Peter promised to notify his boss whenever his work progressed. Peter implemented this promise by periodically calling his boss back via a typed reference like so:

紧耦合

从前,在南方有一块神奇的土地,有一个叫做Peter的工人。他是个勤劳员工,从来不拒绝老板的任何要求。可是他的老板却是个不相信别人的吝啬鬼,为了防止工人偷懒,他坚持要随时知道他们的工作进展。可是Peter却不喜欢老板监视自己的工作。于是Peter向老板保证,一旦工作有任何进展,都会向他报告。于是Peter通过引用一个类[老板],根据工作的进展回调老板[的方法],来实现自己的承诺。

class Worker 
{
	public void Advise(Boss boss) { _boss = boss; }
	public void DoWork() 
	{
		Console.WriteLine("Worker: work started");
		if( _boss != null ) _boss.WorkStarted();
		Console.WriteLine("Worker: work progressing");
		if( _boss != null ) _boss.WorkProgressing();
		Console.WriteLine("Worker: work completed");
		if( _boss != null ) 
		{
			int grade = _boss.WorkCompleted();
			Console.WriteLine("Worker grade= " + grade);
		}
	}
	private Boss _boss;
}

class Boss 
{
	public void WorkStarted() { /* boss doesn't care. */ }
	public void WorkProgressing() { /* boss doesn't care. */ }
	public int WorkCompleted() 
	{
		Console.WriteLine("It's about time!");
		return 2; /* out of 10 */
	}
}

class Universe 
{
	static void Main() 
	{
		Worker  peter = new Worker();
		Boss boss = new Boss();
		peter.Advise(boss);
		peter.DoWork();
		Console.WriteLine("Main: worker completed work");
		Console.ReadLine();
	}
}

Interfaces

Now Peter was a special person. Not only was he able to put up with his mean-spirited boss, but he also had a deep connection with the universe around him. So much so that he felt that the universe was interested in his progress. Unfortunately, there was no way for Peter to advise the Universe of his progress unless he added a special Advise method and special callbacks just for the Universe, in addition to keeping his boss informed. What Peter really wanted to do was to separate the list of potential notifications from the implementation of those notification methods. And so he decided to split the methods into an interface:

接口

现在,Peter只是一个特别的人,他不仅要忍受他那吝啬的老板,还与其周围的宇宙有着紧密的联系。所以他想,可能宇宙也想知道我的工件进展。他怎么才能在保持与老板通信的同时,还能向宇宙报告他的工作进度呢?如果按照原来的想法,他唯一的办法就是:必须专门地添加一个Advise()方法以实现对Universe类的引用,并回调它的方法。于是Peter想到,把所有可能的通知方法从具体的实现中分离出来,作为一个接口[,让老板(BOSS)和宇宙(Universe)来实现,这样他就不用管他通知的是哪一类了]。

interface IWorkerEvents 
{
	void WorkStarted();
	void WorkProgressing();
	int WorkCompleted();
}
class Worker 
{
	public void Advise(IWorkerEvents events) { _events = events; }
	public void DoWork() 
	{
		Console.WriteLine("Worker: work started");
		if( _events != null ) _events.WorkStarted();
		Console.WriteLine("Worker: work progressing");
		if(_events != null ) _events.WorkProgressing();
		Console.WriteLine("Worker: work completed");
		if(_events != null ) 
		{
			int grade = _events.WorkCompleted();
			Console.WriteLine("Worker grade= " + grade);
		}
	}
	private IWorkerEvents _events;
}
class Boss : IWorkerEvents 
{
	public void WorkStarted() { /* boss doesn't care. */ }
	public void WorkProgressing() { /* boss doesn't care. */ }
	public int WorkCompleted() 
	{
		Console.WriteLine("It's about time!");
		return 3; /* out of 10 */
	}
}

Delegates

Unfortunately, Peter was so busy talking his boss into implementing this interface that he didn't get around to notifying the Universe, but he knew he would soon. At least he'd abstracted the reference of his boss far away from him so that others who implemented the IWorkerEvents interface could be notified of his work progress.

Still, his boss complained bitterly. "Peter!" his boss fumed. "Why are you bothering to notify me when you start your work or when your work is progressing?!? I don't care about those events. Not only do you force me to implement those methods, but you're wasting valuable work time waiting for me to return from the event, which is further expanded when I am far away! Can't you figure out a way to stop bothering me?"

And so, Peter decided that while interfaces were useful for many things, when it came to events, their granularity was not fine enough. He wished to be able to notify interested parties only of the events that matched their hearts' desires. So, he decided to break the methods out of the interface into separate delegate functions, each of which acted like a little tiny interface of one method each:

delegate void WorkStarted();
delegate void WorkProgressing();
delegate int WorkCompleted();
委托
不幸的是,当Peter忙于说服彵的老板去实现接口以至于他抽不出身来去通知宇宙,但是他知道不久也是要去的。至少,当老板远离时,他可以转移对老板的引用[即改变_events ——译者注],这样的话,其它实现了IWorkerEvents接口的人也能得到他的工作进程。
可是,他的老板还是不满意。“Peter!”他抱怨道,“为什么每次你开始工作时和工作过程中都要来烦我?!?我不关心这些。你却强迫我实现这些方法,不仅如此,你还在浪费宝贵的时间来等待我的答复,当我远离时,你浪费的时间会更多!你能不能想一个办法,不要老是来打挠我?”
这时,Peter意识到,接口在很多情况下很有用,可是在处理事件时,它还是不够灵活。他希望能够根据别人的需要,只通知他们感兴趣的那一部分事件。于是他决定将接口分离为一个个的委托——就像是一个极小的只包含一个方法的接口。
class Worker 
{
	public void DoWork()
	{
		Console.WriteLine("Worker: work started");
		if( started != null ) started();
		Console.WriteLine("Worker: work progressing");
		if( progressing != null ) progressing();
		Console.WriteLine("Worker: work completed");
		if( completed != null ) 
		{
			int grade = completed();
			Console.WriteLine("Worker grade= " + grade);
		}
	}
	public WorkStarted started;
	public WorkProgressing progressing;
	public WorkCompleted completed;
}
class Boss 
{
	public int WorkCompleted() 
	{
		Console.WriteLine("Better");
		return 4; /* out of 10 */
	}
}
class Universe 
{
	static void Main() 
	{
		Worker  peter = new Worker();
		Boss boss = new Boss();
		peter.completed = new WorkCompleted(boss.WorkCompleted);
		peter.DoWork();
		Console.WriteLine("Main: worker completed work");
		Console.ReadLine();
	}
}

Static Listeners

This accomplished the goal of not bothering his boss with events that he didn't want, but still Peter had not managed to get the universe on his list of listeners. Since the universe is an all-compassing entity, it didn't seem right to hook delegates to instance members (imagine how many resources multiple instances of the universe would need...). Instead, Peter need to hook delegates to static members, which delegates support fully:

静态监听者

这样,他虽然实现了不再拿老板不关心的事件去烦他的目的,可是他还是没能把宇宙它加到他的监听者列表中去。因为宇宙是一个包含一切的实体,似乎不适于把它实例化后挂钩到委托[即委托对方法的引用——译者注](想象一下,实例化上一个宇宙将需要多少资源)。事实上,Peter需要将委托挂钩到静态成员:

class Universe 
{
	static void WorkerStartedWork() 
	{
		Console.WriteLine("Universe notices worker starting work");
	}

	static int WorkerCompletedWork() 
	{
		Console.WriteLine("Universe pleased with worker's work");
		return 7;
	}

	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: worker completed work");
		Console.ReadLine();
	}
}

Events

Unfortunately, the Universe being very busy and unaccustomed to paying attention to individuals, has managed to replace Peter's boss's delegate with its own. This is an unintended side effect of making the delegate fields public in Peter's Worker class. Likewise, if Peter's boss gets impatient, he can decide to fire Peter's delegates himself (which is just the kind of rude thing that Peter's boss was apt to do):

// Peter's boss taking matters into his own hands
if( peter.completed != null ) peter.completed();

Peter wants to make sure that neither of these can happens. He realizes he needs to add registration and unregistration functions for each delegate so that listeners can add or remove themselves, but can't clear the entire list or fire Peter's events. Instead of implementing these functions himself, Peter uses the event keyword to make the C# compiler build these methods for him:

事件

不幸的是,宇宙很忙,也不习惯去注意细节,把peter.completed委托对boss.WorkCompleted的引用替换成了对自己方法的引用。这是把Worker类的委托域作为公有[public]的一个(并因为不作任何限定而造成的)无意识的不良影响。同样(因为这),如果Peter的老板没有耐性了,他也可以决定亲自来引发Peter的委托(这也是Peter的老板惯于做的粗鲁的事)

//Peter's boss taking matters into his own hands 
if( peter.completed != null ) peter.completed();

Peter想确保这些都不要发生。他意识到,他需要为每个委托添加注册和注销[对方法的引用]的方法,这样的话监听者可以自行添加或是移除监听了,但是不能清空整个监听者列表或自己来引发Peter的事件。Peter使用了关键字event让C#编译器为他构建而不是自己亲自去实现这现方法:

class Worker 
{
	public event WorkStarted started;
	public event WorkProgressing progressing;
	public event WorkCompleted completed;
}

Peter knows that the event keyword erects a property around a delegate, only allowing C# clients to add or remove themselves with the += and -= operators, forcing his boss and the universe to play nicely
Peter知道,event关键字给delegate加了一个特性,只允监听者使用+=和-=操作符来添加和移除自己,要求他的老板和宇宙必须遵守这一点:
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: worker completed work");
	Console.ReadLine();
}

Harvesting All Results

At this point, Peter breathes a sign of relief. He has managed to satisfy the requirements of all his listeners without having to be closely coupled with the specific implementations. However, he notices that while both his boss and the universe provide grades of his work that he's only receiving one of the grades. In the face of multiple listeners, he'd really like to harvest all of their results. So, he reaches into his delegate and pulls out the list of listeners so that he can call each of them manually:

获得所有结果

现在,Peter终于可以轻轻地叹口气了。他成功地满足了所有监听者的要求,而且避免了由于特定实现而引起的紧耦合。然而,他注意到,他的老板的宇宙都给他的工作打了分,但是他只收到了一个。当有多个监听者时,他想获得所有的结果。于是,他深入到委托的内部,获得监听者列表然后手工地一个个的调用:

public void DoWork() 
{
	Console.WriteLine("Worker: work completed");
	if( completed != null ) 
	{
		foreach( WorkCompleted wc in completed.GetInvocationList() ) 
		{
			int grade = wc();
			Console.WriteLine("Worker grade= " + grade);
		}
	}
}

Async Notification: Fire & Forget

In the meantime, his boss and the universe have been distracted with other things, which means that the time it takes them to grade Peter's work is greatly expanded:

异步通知:引发与遗忘

然而这时,他的老板和宇宙正忙于其它事情,这就意味着他们要很长的一段时间才能给Peter的工作打分:

class Boss 
{
	public int WorkCompleted() 
	{
		System.Threading.Thread.Sleep(3000);
		Console.WriteLine("Better"); return 6; /* out of 10 */
	}
}
class Universe 
{
	static int WorkerCompletedWork() 
	{
		System.Threading.Thread.Sleep(4000);
		Console.WriteLine("Universe is pleased with worker's work");
		return 7;
	}
}

Unfortunately, since Peter is notifying each listener one at a time, waiting for each to grade him, these notifications now take up quite a bit of his time when he should be working. So, he decides to forget the grade and just fire the event asynchronously:

不幸的是,彼得每次通知一个监听者后,必须等待它给自己打分,这些通知占用了他太多的工作事件。于是他决定忘掉分数,仅仅异步激发事件:

public void DoWork() 
{
	Console.WriteLine("Worker: work completed");
	if( completed != null ) 
	{
		foreach( WorkCompleted wc in completed.GetInvocationList() )
		{
			wc.BeginInvoke(null, null);
		}
	}
}

Async Notification: Polling

This allows Peter to notify the listeners while letting Peter get back to work immediately, letting the process thread pool invoke the delegate. Over time, however, Peter finds that he misses the feedback on his work. He knows that he does a good job and appreciates the praise of the universe as a whole (if not his boss specifically). So, he fires the event asynchronously, but polls periodically, looking for the grade to be available:

异步通知:轮询

这样,Peter可以在通知了监听者这后立即回到自己的工作,让进程的线程池来调用这些代理。然而,慢慢地,由于他丢失了对他工作的评价,Peter意识到做好一份工作和体味来自宇宙的赞扬同样重要(尤其在没有获得他老板的评价时)。于是他异步地引发事件,同时周期性地轮询,以得到所有的分数。

public void DoWork() 
{
	Console.WriteLine("Worker: work completed");
	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("Worker grade= " + grade);
		}
	}
}

Async Notification: Delegates

Unfortunately, Peter is back to what he wanted his boss to avoid with him in the beginning, i.e. looking over the shoulder of the entity doing the work. So, he decides to employ his own delegate as a means of notification when the async delegate has completed, allowing him to get back to work immediately, but still be notified when his work has been graded:

这样,Peter又回到了他一开始就想他的老板所要避免的问题,例如一直监视着他的工作[意思是Peter必须一直监视监听者是否已经完成打分——译者注]。于是,他决定使用他自己的委托,当异步调用的[打分]方法完成时来通知自己,这样他就可以立即回到工作,但是仍可以在工作被打分后得到通知:

public void DoWork() 
{
	Console.WriteLine("Worker: work completed");
	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("Worker grade= " + grade);
}

Happiness in the Universe

Peter, his boss and the universe are finally satisfied. Peter's boss and the universe are allowed to be notified of the events that interest them, reducing the burden of implementation and the cost of unnecessary round-trips. Peter can notify them each, ignoring how long it takes them to return from their target methods, while still getting his results asynchronously. Peter knows that it's not *quite* that easy, because as soon as he fires events asynchronously, the target methods are likely to be executed on another thread, as is Peter's notification of when the target method has completed. However, Peter is good friends with Mike, who is very familiar with threading issues and can provide guidance in that area.

And they a lived happily every after. The end.

宇宙中的幸福

Peter,他的老板和宇宙都非常满意。Peter的老板和宇宙可以只得到他们感兴趣的事件的通知,减少了实现的负担以及通信时往返的差旅费。Peter可以分别通知它们,而不用理会需要多长时间从目标方法返回,却可以异步也得到结果。Peter知道事情并没有这么简单,因为他异步地引发事件时,目标方法会在另一个线程中执行,当目标方法完成时Peter获利分数也是一样。然而,Peter的好朋友Mike擅长于处理线程事务,可以在这一领域给Peter提供帮助。

最后,他们从此幸福地生活下去。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值