C#:事件还是观察者接口? 优点缺点?
我有以下内容(简化):
interface IFindFilesObserver
{
void OnFoundFile(FileInfo fileInfo);
void OnFoundDirectory(DirectoryInfo directoryInfo);
}
class FindFiles
{
IFindFilesObserver _observer;
// ...
}
......我很矛盾。 这基本上是我用 C++ 编写的内容,但 C# 有事件。 我应该更改代码以使用事件,还是应该保留它?
与传统的观察者界面相比,事件的优点或缺点是什么?
I've got the following (simplified):
interface IFindFilesObserver
{
void OnFoundFile(FileInfo fileInfo);
void OnFoundDirectory(DirectoryInfo directoryInfo);
}
class FindFiles
{
IFindFilesObserver _observer;
// ...
}
...and I'm conflicted. This is basically what I would have written in C++, but C# has events. Should I change the code to use events, or should I leave it alone?
What are the advantages or disadvantages of events over a traditional observer interface?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
将事件视为回调接口,其中该接口只有一个方法。
仅挂钩您需要的事件
对于事件,您只需要为您感兴趣的事件实现处理程序。 在观察者接口模式中,您必须实现整个接口中的所有方法,包括实现您实际上并不关心处理的通知类型的方法体。 在您的示例中,您始终必须实现 OnFoundDirectory 和 OnFoundFile,即使您只关心这些事件之一。
减少维护
事件的另一个好处是您可以向特定类添加一个新事件,以便它引发它,并且您不必更改每个现有的观察者。 然而,如果您想向接口添加新方法,则必须遍历已经实现该接口的每个类并在所有类中实现新方法。 不过,对于事件,您只需要更改实际想要执行某些操作以响应您添加的新事件的现有类。
该模式内置于语言中,因此每个人都知道如何使用它
事件是惯用的,因为当您看到事件时,您就知道如何使用它。 通过观察者接口,人们经常实现不同的注册方式来接收通知并连接观察者。但是,一旦您学会了如何注册和使用其中一种方式(使用 += 运算符),其余的都是相同的。
界面优点
我在界面方面没有太多专业人士。 我猜他们强迫某人实现接口中的所有方法。 但是,你不能真正强迫某人正确实现所有这些方法,所以我认为这没有太多价值。
语法
有些人不喜欢为每个事件声明委托类型的方式。 此外,.NET 框架中的标准事件处理程序具有以下参数:(对象发送者、EventArgs args)。 由于发送方没有指定特定类型,因此如果您想使用它,则必须进行向下转换。 这在实践中通常很好,但感觉不太正确,因为你失去了静态类型系统的保护。 但是,如果您实现自己的事件并且不遵循 .NET 框架约定,则可以使用正确的类型,因此不需要潜在的向下转换。
Consider an event to be a callback interface where the interface has only one method.
Only hook events you need
With events, you only need to implement handlers for events you're interested in handling. In the observer interface pattern, you'd have to implement all methods in the entire interface including implementing method bodies for notification types you don't actually care about handling. In your example, you always have to implement OnFoundDirectory and OnFoundFile, even if you only care about one of these events.
Less maintenance
Another good thing about events is you can add a new one to a particular class so that it will raise it, and you don't have to change every existing observer. Whereas if you want to add a new method to an interface, you have to go around every class that already implements that interface and implement the new method in all of them. With an event though, you only need to alter existing classes that actually want to do something in response to the new event you're adding.
The pattern is built into the language so everybody knows how to use it
Events are idiomatic, in that when you see an event, you know how to use it. With an observer interface, people often implement different ways of registering to receive notifications and hook up the observer.. with events though, once you've learnt how to register and use one (with the += operator), the rest are all the same.
Pros for interfaces
I haven't got many pros for interfaces. I guess they force someone to implement all methods in the interface. But, you can't really force somebody to implement all those methods correctly, so I don't think there's a lot of value on this.
Syntax
Some people don't like the way you have to declare a delegate type for each event. Also, standard event handlers in the .NET framework have these parameters: (object sender, EventArgs args). As sender doesn't specify a particular type, you have to down-cast if you want to use it. This often is fine in practice, it feels not quite right though because you're losing the protection of the static type system. But, if you implement your own events and don't follow the .NET framework convention on this, you can use the correct type so potential down-casting isn't required.
嗯,事件可以用来实现观察者模式。 事实上,使用事件可以被视为观察者模式的另一种实现恕我直言。
Hmm, events can be used to implement the Observer pattern. In fact, using events can be regarded as another implementation of the observer-pattern imho.
事件比简单函数调用慢 2 倍,如果每次引发时都进行空检查,并在空检查和调用之前复制事件委托以使其线程安全,则慢 3 倍。
另外阅读 MSDN 了解新的(4.0 中)
IObserver;
接口。考虑以下示例:
Events are 2x time slower than simple function call, 3x slower if you do null check on every raise, and copy event delegate before null check and invocation to make it thread safe.
Also read MSDN about new (in 4.0)
IObserver<T>
interface.Consider this example:
接口解决方案的优点:
缺点:
Pros of an interface-solution:
Cons:
活动的一些进一步好处。
您可以自己实现所有这些(工具链除外),但这非常困难。 例如:
如果您使用像 List<> 这样的成员变量 存储观察者列表。
如果您使用 foreach 对其进行迭代,那么在 OnFoo() 方法回调之一中添加或删除订阅者的任何尝试都将触发异常,除非您编写进一步的代码来干净地处理它。
Some further benefits of events.
You can achieve all of these (except the tool chain) yourself but it's surprisingly hard. For example:
If you use a member variable like a List<> to store the list of observers.
If you use foreach to iterate over it then any attempt to add or remove a subscriber within one of the OnFoo() method callbacks will trigger an exception unless you write further code to deal with it cleanly.
最好的决定方法是:哪一个更适合具体情况。 这听起来可能是一个愚蠢或无益的答案,但我认为您不应该将其中一个视为“正确”的解决方案。
我们可以向您提供一百个提示。 当观察者期望监听任意事件时,事件是最好的。 当观察者希望列出所有给定的事件集时,界面是最好的。 处理 GUI 应用程序时,事件是最好的选择。 接口消耗更少的内存(多个事件的单个指针)。 亚达亚达亚达。 优点和缺点的项目符号列表值得考虑,但不是明确的答案。 您真正需要做的是在实际应用中尝试它们并获得良好的感受。 然后你可以选择更适合情况的一种。 学习形式实践。
如果您必须使用单个定义问题,那么问问自己哪个更能描述您的情况:一组松散相关的事件,其中任何一个都可以使用或忽略,或者一组密切相关的事件,通常都需要由一名观察员。 但是,我只是描述了事件模型和接口模型,所以我又回到了第一个问题:哪一个更适合这种情况?
The best way to decide is this: which one suits the situation better. That might sound like a silly or unhelpful answer, but I don't think you should regard one or the other as the "proper" solution.
We can throw a hundred tips at you. Events are best when the observer is expected to listen for arbitrary events. An interface is best when the observer is expected to listed to all of a given set of events. Events are best when dealing with GUI apps. Interfaces consume less memory (a single pointer for multiple events). Yadda yadda yadda. A bulleted list of pros and cons is something to think about, but not a definitive answer. What you really need to do is try both of them in actual applications and get a good feel for them. Then you can choose the one that suits the situation better. Learn form doing.
If you have to use a single defining question, then ask yourself which better describes your situation: A set of loosely related events any of which may be used or ignored, or a set of closely related events which will all generally need to be handled by one observer. But then, I'm just describing the event model and interface model, so I'm back at square one: which one suits the situation better?
优点是事件更加“点网状”。 如果您正在设计可以拖放到表单上的非可视组件,则可以使用设计器将它们连接起来。
缺点是一个事件仅表示一个事件 - 您需要为每个要通知观察者的“事物”一个单独的事件。 这实际上并没有太大的实际影响,只是每个被观察的对象都需要为每个事件的每个观察者保存一个引用,在有大量被观察的对象的情况下会导致内存膨胀(这是他们采用不同方式的原因之一)管理 WPF 中的观察者/可观察关系)。
就你而言,我认为这没有多大区别。 如果观察者通常对所有这些事件感兴趣,请使用观察者界面而不是单独的事件。
Pros are that events are more 'dot-netty'. If you are designing non-visual components that can be dropped onto a form, you can hook them up using the designer.
Cons are that an event only signifies a single event - you need a separate event for each 'thing' that you want to notify the observer about. This doesn't really have much practical impact except that each observed object would need to hold a reference for every observer for every event, bloating memory in the case where there are lots of observed objects (one of the reasons they made a different way of managing the observer/observable relationship in WPF).
In your case I'd argue it doesn't make much difference. If the observer would typically be interested in all those events, use an observer interface rather than separate events.
我更喜欢基于事件的解决方案,原因如下:
I prefer an event base solution for the following reasons
Java 具有对匿名接口的语言支持,因此回调接口是 Java 中使用的东西。
C# 支持匿名委托 - lambda - 因此事件是 C# 中使用的东西。
Java has language support for anonymous interfaces, so callback interfaces are the thing to use in Java.
C# has support for anonymous delegates - lambdas - and so events are the thing to use in C#.
接口的一个好处是它们更容易应用装饰器。 标准示例:
相比:(
我是装饰器模式的忠实粉丝)。
A benefit of interfaces is that they are easier to apply decorators to. The standard example:
compared to:
(I'm a big fan of the decorator pattern).
如果您的对象需要以某种保留引用的方式进行序列化,例如 NetDataContractSerializer 或 protobuf 事件将无法跨越序列化边界。 由于观察者模式仅依赖于对象引用,因此如果需要的话,它可以毫无问题地处理这种类型的序列化。
前任。 您有一堆相互双向链接的业务对象,您需要将它们传递给 Web 服务。
If your objects will need to be serialized in some way that retains references such as with NetDataContractSerializer or perhaps protobuf events will not be able to cross the serialization boundary. Since observer pattern relies on nothing more than just object references, it can work with this type of serialization with no problem if that is what is desired.
Ex. You have a bunch of business objects that link to each other bidirectionally that you need to pass to a web service.