我有以下(简化):
interface IFindFilesObserver { void OnFoundFile(FileInfo fileInfo); void OnFoundDirectory(DirectoryInfo directoryInfo); } class FindFiles { IFindFilesObserver _observer; // ... }
......而且我很矛盾.这基本上就是我用C++编写的,但C#有事件.我应该更改代码以使用事件,还是应该不管它?
与传统观察者界面相比,事件的优缺点是什么?
将事件视为回调接口,其中接口只有一个方法.
只有您需要的钩子事件
使用事件,您只需要为您感兴趣的事件实现处理程序.在观察者界面模式中,您必须在整个界面中实现所有方法,包括实现您实际上并不关心处理的通知类型的方法体.在您的示例中,您始终必须实现OnFoundDirectory和OnFoundFile,即使您只关心其中一个事件.
减少维护
事件的另一个好处是,您可以向特定类添加一个新的,以便它可以提升它,并且您不必更改每个现有的观察者.而如果要向接口添加新方法,则必须绕过已实现该接口的每个类并在所有接口中实现新方法.但是,对于事件,您只需要更改实际想要执行某些操作的现有类以响应您要添加的新事件.
模式内置于语言中,因此每个人都知道如何使用它
事件是惯用的,因为当您看到事件时,您就知道如何使用它.使用观察者界面,人们经常实现不同的注册方式来接收通知并连接观察者......但是,一旦你学会了如何注册和使用一个(使用+ =运算符),其余的都是相同.
接口的优点
我没有很多接口的专业人士.我猜他们强迫某人在界面中实现所有方法.但是,你不能真正强迫某人正确地实施所有这些方法,所以我认为这没有很多价值.
语法
有些人不喜欢为每个事件声明委托类型的方式.此外,.Net框架中的标准事件处理程序遵循以下参数:( object sender,EventArgs args).由于发件人未指定特定类型,因此如果要使用它,则必须进行向下转换.这在实践中通常很好,如果感觉不太正确,因为你正在失去对静态类型系统的保护.但是,如果您实现自己的事件并且不遵循.Net框架约定,则可以使用正确的类型,因此不需要潜在的向下转换.
嗯,事件可用于实现Observer模式.事实上,使用事件可以被视为观察者模式imho的另一种实现.
界面解决方案的优点:
如果添加方法,现有观察者需要实现这些方法.这意味着您不太可能忘记将现有观察者连接到新功能.你当然可以将它们作为空方法来实现,这意味着你可以在某些"事件"的作用下仍然无所事事.但你不会那么容易忘记.
如果使用显式实现,则还会以其他方式获得编译器错误,如果删除或更改现有接口,则实现它们的观察者将停止编译.
缺点:
更多的想法必须进入规划,因为观察者界面的变化可能会在整个解决方案中强制执行更改,这可能需要不同的计划.由于简单事件是可选的,因此除非其他代码应对事件做出反应,否则很少或不需要更改其他代码.
事件的一些进一步好处.
您可以免费获得适当的组播行为.
如果您更改事件的订阅者以响应该事件,则行为已明确定义
它们可以轻松而一致地进行内省(反映)
事件的工具链支持(仅仅因为它们是.net中的成语)
您可以选择使用它提供的异步apis
你可以自己实现所有这些(工具链除外),但这是非常困难的.例如:如果使用像List <>这样的成员变量来存储观察者列表.如果你使用foreach迭代它,那么任何在一个OnFoo()方法回调中添加或删除订阅者的尝试都将触发异常,除非你编写更多代码来干净地处理它.
事件很难通过对象链传播,例如,如果您使用FACADE模式或将工作委托给其他类.
您需要非常小心地从事件中取消订阅以允许对象被垃圾收集.
事件比简单函数调用慢2倍,如果对每次加注进行空检查,则慢3倍,并在null检查和调用之前复制事件委托以使其线程安全.
另请阅读有关新(4.0)IObserver
界面的MSDN .
考虑这个例子:
using System; namespace Example { //Observer public class SomeFacade { public void DoSomeWork(IObserver notificationObject) { Worker worker = new Worker(notificationObject); worker.DoWork(); } } public class Worker { private readonly IObserver _notificationObject; public Worker(IObserver notificationObject) { _notificationObject = notificationObject; } public void DoWork() { //... _notificationObject.Progress(100); _notificationObject.Done(); } } public interface IObserver { void Done(); void Progress(int amount); } //Events public class SomeFacadeWithEvents { public event Action Done; public event ActionProgress; private void RaiseDone() { if (Done != null) Done(); } private void RaiseProgress(int amount) { if (Progress != null) Progress(amount); } public void DoSomeWork() { WorkerWithEvents worker = new WorkerWithEvents(); worker.Done += RaiseDone; worker.Progress += RaiseProgress; worker.DoWork(); //Also we neede to unsubscribe... worker.Done -= RaiseDone; worker.Progress -= RaiseProgress; } } public class WorkerWithEvents { public event Action Done; public event Action Progress; public void DoWork() { //... Progress(100); Done(); } } }