何时使用事件或回调通知
主题“使用回调的异步通知”和“使用事件的异步通知”演示表明回调所要实现的工作比事件多。但不应只是根椐工作量来决定使用哪一种方式。回调和事件代表不同的通讯方式,应选择最适合需要的。
可将事件和回调之间的差别特征化:事件象匿名广播,而回调象一次握手。
由此可知,引发事件的部件对其客户端一无所知,而进行回调的部件却知之甚详。
对于开发人员意味着:
- 客户端引用一个引发事件的对象,对这个客户端,它可将引用放置在 WithEvents 变量中来处理那些对象。引发事件的对象没有有关其客户端的信息。它向未知数目的听众进行广播, 剧院中可能一个观众都没有。
与此相对,进行回调的部件必须引用它将调用的每一个对象。它必须准确了解有多少对象。
- 引发事件的对象不会控制接收事件的客户端次序。(应小心避免与可能要遵守的次序发生关系。)
与此相对,进行回调的部件可以控制回调客户程序的次序。例如,可以给某些客户程序以更高优先权。
- 当对象引发事件时,其所有客户端都在引发事件的对象再次获得控制之前处理该事件。
与此相对,进行回调的部件在每次调用客户程序之后进行控制。
- 如果事件包含 ByRef 参数,则该参数可被任何处理事件的客户程序改变。只有最后的客户端进行的改变才对引发事件的对象可见,因为(如上所述),直到所有客户端都处理该事件之前,引发事件的对象不会再度获得控制。
与此相对,进行回调的部件在每次调用客户端之后可检查 ByRef 参数的变化。
- 如果在客户端的处理程序中发生未处理的错误,则引发事件的对象就不能接收错误。如果对象是由在客户端地址空间中运行的进程内部件提供的,则客户端和部件都会由于未处理的错误而终止。
与此相对,进行回调的部件将接收回调方法中发生的错误,并且必须准备处理它们。
注意 事件与回调的另一差别在于,事件不能具有可选参数、命名参数或 ParamArray 参数。
- 部件可用回调提供一些通知,也可用事件提供一些通知。通知的特点决定要使用什么方式。当下列所有命题为真时,应使用事件来提供通知:
- 可匿名广播通知。
- 客户端接收通知的次序不重要。
- 直到知道所有客户端接收到通知之前,部件都不必再获得控制。
- 如果通知涉及 ByRef 参数,且在每个客户端接收到通知之后,部件不必测试这些参数的值。部件只需知道最后一个指定值。(可安排客户端在使用 ByRef 参数时进行合作,例如,一旦将 Cancel 参数设置成 True,客户端就不能改变它;但无法硬性这样做。)
- 部件不必知道客户端中发生的错误。
如果上述命题中有一为假,则应做额外工作使用回调方法来提供通知
当执行任务性能极为关键时,也可使用回调方法来做额外工作。使用 Implements 命题将回调接口添加到客户程序的回调对象上,就可得到具有回调方法的 vtable 绑定。事件是不会被 vtable 绑定的。(对提供事件或回调的进程内部件,这点尤为显著。)