Item 21: Express Callbacks with Delegates
用委托来表示回调
Me: "Son, go mow the yard. I'm going to read for a while."
Me:“儿子,取把院子里面的草割了。我看会儿书。”
Scott: "Dad, I cleaned up the yard."
Scott: “爸爸,我已经清理完了院子。”
Scott: "Dad, I put gas in the mower."
Scott: “爸爸,我给割草机加油”
Scott: "Dad, the mower won't start."
Scott: “爸爸,割草机不工作了。”
Me: "I'll start it."
Me:“我来弄。”
Scott: "Dad, I'm done."
Scott: “爸爸,我搞定了”
This little exchange illustrates callbacks. I gave my son a task, and he (repeatedly) interrupted me with the status. I did not block my own progress while I waited for him to finish each part of the task. He was able to interrupt me periodically when he had an important (or even unimportant) status to report or needed my assistance. Callbacks are used to provide feedback from a server to a client asynchronously. They might involve multithreading, or they might simply provide an entry point for synchronous updates. Callbacks are expressed using delegates in the C# language.
这个小小的交流举例说明了回调。我给我儿子一个任务,他(重复的)打断我,告诉我他的状态。在等待他完成工作的每一步时,我没有阻塞自己的进度。当他有重要的(甚至不重要的)状态要报告,或者需要我帮助的时候,可以周期性的打断我。回调可以用来从服务器向客户异步的提供反馈。它们可能涉及到多线程,或者简单的为同步更新提供一个切入点。在C#语言里面,回调通过委托来表述。
Delegates provide type-safe callback definitions. Although the most common use of delegates is events, that should not be the only time you use this language feature. Any time you need to configure the communication between classes and you desire less coupling than you get from interfaces, a delegate is the right choice. Delegates let you configure the target at runtime and notify multiple clients. A delegate is an object that contains a reference to a method. That method can be either a static method or an instance method. Using the delegate, you can communicate with one or many client objects, configured at runtime.
委托提供类型安全的回调定义。尽管委托最常用的用法是事件,但是那不应该是你使用该语言特性的唯一时候。任何时候,当你需要配置类之间通信的时候,当你渴望得到比接口具有更低耦合性的时候,委托都是正确的选择。委托让你可以在运行时配置目标,并通知多个客户。委托是包含有对一个方法的引用的对象。那个方法既可以是静态方法,也可以是实例方法。使用委托,你可以和一个或者多个客户对象通信,在运行时进行配置。
Multicast delegates wrap all the functions that have been added to the delegate in a single function call. Two caveats apply to this construct: It is not safe in the face of exceptions, and the return value will be the return value of the last function invocation.
多播委托将添加给委托的所有方法包装在一个单独的方法调用里面。对于这个结构有2个警告:在异常面前不安全;返回值是最后一个方法调用的返回值。
Inside a multicast delegate invocation, each target is called in succession. The delegate does not catch any exceptions. Therefore, any exception that the target throws ends the delegate invocation chain.
在多播委托的内部,每个对象会被连续的调用。委托不捕捉任何异常。因此,目标抛出的任何异常都会终止委托链的调用。
A similar problem exists with return values. You can define delegates that have return types other than void. You could write a callback to check for user aborts:
在返回值上也存在相似的问题。你可以定义有返回值的委托而不是无返回值的。你可以编写回调来检查用户的放弃操作。
- public void LengthyOperation(ContinueProcessing pred)
- {
- foreach (ComplicatedClass cl in container)
- {
- cl.DoLengthyOperation();
- // Check for user abort:
- if (false == pred())
- return;
- }
- }
It works as a single delegate, but using it as a multicast is problematic:
它像单个委托一样工作,像多播委托一样使用是有问题的:
- ContinueProcessing cp = new ContinueProcessing(CheckWithUser);
- cp += new ContinueProcessing(CheckWithSystem);
- c.LengthyOperation(cp);
The value returned from invoking the delegate is the return value from the last function in the multicast chain. All other return values are ignored. The return from the CheckWithUser() predicate is ignored.
调用委托返回的值是在多播委托链里面最后一个方法的返回值,所有其它的返回值都被忽略。从CheckWithUser()断言返回的值被忽略。
You address both issues by invoking each delegate target yourself. Each delegate you create contains a list of delegates. To examine the chain yourself and call each one, iterate the invocation list yourself:
通过自己调用每个委托目标,你同时表现了这2个问题。你创建的每个委托都包含一个委托列表。为了自己检查链,并调用每一个,需要自己迭代调用列表。
- public delegate bool ContinueProcessing();
- public void LengthyOperation(ContinueProcessing pred)
- {
- bool bContinue = true;
- foreach (ComplicatedClass cl in container)
- {
- cl.DoLengthyOperation();
- foreach (ContinueProcessing pr in pred.GetInvocationList())
- bContinue &= pr();
- if (false == bContinue)
- return;
- }
- }
In this case, I've defined the semantics so that each delegate must be true for the iteration to continue.
在这情况下,我已经定义了这个语法,那样,迭代要继续下去的话,每个委托都必须为true。
Delegates provide the best way to utilize callbacks at runtime, with simpler requirements on client classes. You can configure delegate targets at runtime. You can support multiple client targets. Client callbacks should be implemented using delegates in .NET.
委托为在运行时利用回调提供了最好的方法,在客户类上只需要简单的要求。可以在运行时配置委托目标。可以支持多播委托。在.Net里面客户回调应该使用委托来实现。