C#:事件还是观察者接口?优点缺点?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/550785/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 07:49:47  来源:igfitidea点击:

C#: events or an observer interface? Pros/cons?

c#eventsobserver-pattern

提问by Roger Lipscombe

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?

……我很矛盾。这基本上就是我用 C++ 编写的内容,但是 C# 有事件。我应该更改代码以使用事件,还是应该不理会它?

What are the advantages or disadvantages of events over a traditional observer interface?

与传统观察者接口相比,事件有哪些优点或缺点?

采纳答案by Scott Langham

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.

仅挂钩您需要的
事件 对于事件,您只需为您感兴趣的事件实现处理程序。在观察者接口模式中,您必须在整个接口中实现所有方法,包括为您实际上并不关心处理的通知类型实现方法主体。在您的示例中,您始终必须实现 OnFoundDirectory 和 OnFoundFile,即使您只关心这些事件之一。

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 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 follow 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, if 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.

语法
有些人不喜欢您必须为每个事件声明委托类型的方式。此外,.Net 框架中的标准事件处理程序具有以下参数:(对象发送者,EventArgs args)。由于发件人没有指定特定类型,如果你想使用它,你必须向下转换。这在实践中通常很好,但如果感觉不太正确,因为您正在失去静态类型系统的保护。但是,如果您实现自己的事件并且不遵循 .Net 框架约定,您可以使用正确的类型,因此不需要潜在的向下转换。

回答by Frederik Gheysels

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.

嗯,事件可以用来实现观察者模式。实际上,使用事件可以看作是观察者模式 imho 的另一种实现。

回答by U62

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.

优点是事件更像是“dot-netty”。如果您正在设计可以放置在表单上的非可视组件,您可以使用设计器将它们连接起来。

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).

缺点是事件仅表示单个事件 - 您需要为每个要通知观察者的“事物”单独事件。这实际上并没有太大的实际影响,除了每个被观察对象需要为每个事件的每个观察者保存一个引用,在有很多被观察对象的情况下内存膨胀(这是他们采用不同方式的原因之一)在 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.

在你的情况下,我认为它没有太大区别。如果观察者通常对所有这些事件感兴趣,请使用观察者接口而不是单独的事件。

回答by Lasse V. Karlsen

Pros of an interface-solution:

接口解决方案的优点:

  • If you add methods, existing observers needs to implement those methods. This means that you have less of a chance of forgetting to wire up existing observers to new functionality. You can of course implement them as empty methods which means you have the luxury of still doing nothing in response to certain "events". But you won't so easily forget.
  • If you use explicit implementation, you'll also get compiler errors the other way, if you remove or change existing interfaces, then observers implementing them will stop compiling.
  • 如果添加方法,现有的观察者需要实现这些方法。这意味着您不太可能忘记将现有观察者连接到新功能。当然,您可以将它们实现为空方法,这意味着您仍然可以对某些“事件”不做任何事情。但你不会那么容易忘记。
  • 如果您使用显式实现,您也会以另一种方式获得编译器错误,如果您删除或更改现有接口,那么实现它们的观察者将停止编译。

Cons:

缺点:

  • More thought has to go into planning, since a change in the observer interface might enforce changes all over your solution, which might require different planning. Since a simple event is optional, little or no other code has to change unless that other code should react to the event.
  • 必须更多地考虑计划,因为观察者界面的更改可能会强制更改整个解决方案,这可能需要不同的计划。由于简单事件是可选的,除非其他代码应对该事件做出反应,否则几乎不需要更改其他代码。

回答by JaredPar

I prefer an event base solution for the following reasons

由于以下原因,我更喜欢基于事件的解决方案

  • It reduces the cost of entry. It's much easier to say "+= new EventHandler" than to implement a full fledged interface.
  • It reduces maintenance costs. If you add a new event into your class that's all that needs to be done. If you add a new event to an interface you must update every single consumer in your code base. Or define an entirely new interface which over time gets annoying to consumers "Do I implement IRandomEvent2 or IRandomEvent5?"
  • Events allow for handlers to be non-class based (ie a static method somewhere). There is no functional reason to force all event handlers to be an instance member
  • Grouping a bunch of events into an interface is making an assumption about how the events are used (and it's just that, an assumption)
  • Interfaces offer no real advantage over a raw event.
  • 它降低了进入成本。说“+= new EventHandler”比实现一个完整的接口要容易得多。
  • 它降低了维护成本。如果您将一个新事件添加到您的类中,这就是所有需要完成的工作。如果向接口添加新事件,则必须更新代码库中的每个消费者。或者定义一个全新的接口,随着时间的推移它会让消费者感到厌烦“我是实现 IRandomEvent2 还是 IRandomEvent5?”
  • 事件允许处理程序基于非类(即某处的静态方法)。没有强制所有事件处理程序成为实例成员的功能性原因
  • 将一堆事件分组到一个接口中是对如何使用事件做出假设(这只是一个假设)
  • 与原始事件相比,接口没有真正的优势。

回答by Daniel Earwicker

Java has language support for anonymous interfaces, so callback interfaces are the thing to use in Java.

Java 对匿名接口有语言支持,因此回调接口是 Java 中使用的东西。

C# has support for anonymous delegates - lambdas - and so events are the thing to use in C#.

C# 支持匿名委托 - lambdas - 所以事件是在 C# 中使用的东西。

回答by snarf

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.

我们可以向您抛出一百条提示。当期望观察者监听任意事件时,事件是最好的。当期望观察者列出所有给定的事件集时,接口是最好的。在处理 GUI 应用程序时,事件是最好的。接口消耗更少的内存(多个事件的单个指针)。亚达亚达亚达。优点和缺点的项目符号列表值得考虑,但不是确定的答案。您真正需要做的是在实际应用中尝试这两种方法,并对它们有一个很好的感觉。然后您可以选择更适合情况的一种。学习形式做。

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?

如果您必须使用一个定义问题,那么问问自己哪个更能描述您的情况:一组松散相关的事件,其中任何一个都可能被使用或忽略,或者一组密切相关的事件,这些事件通常都需要由一名观察员。但是,我只是在描述事件模型和接口模型,所以我又回到了第一点:哪一个更适合这种情况?

回答by ShuggyCoUk

Some further benefits of events.

事件的一些进一步好处。

  • You get proper multicast behaviour for free.
  • If you change the subscribers of an event in response to that event the behaviour is well defined
  • They can be introspected (reflected) easily and consistently
  • Tool chain support for events (simply because they are the idiom in .net)
  • You get the option to use the asynchronous apis it provides
  • 您可以免费获得适当的多播行为。
  • 如果您更改事件的订阅者以响应该事件,则行为是明确定义的
  • 他们可以轻松且一致地被内省(反映)
  • 事件的工具链支持(仅仅是因为它们是 .net 中的习惯用法)
  • 您可以选择使用它提供的异步 API

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.

您可以自己实现所有这些(工具链除外),但这出奇地难。例如:如果使用像 List<> 这样的成员变量来存储观察者列表。如果您使用 foreach 对其进行迭代,则任何在 OnFoo() 方法回调中添加或删除订阅者的尝试都将触发异常,除非您编写进一步的代码来干净地处理它。

回答by Andreas

A benefit of interfaces is that they are easier to apply decorators to. The standard example:

接口的一个好处是它们更容易应用装饰器。标准示例:

subject.RegisterObserver(new LoggingObserver(myRealObserver));

compared to:

相比:

subject.AnEvent += (sender, args) => { LogTheEvent(); realEventHandler(sender, args); };

(I'm a big fan of the decorator pattern).

(我是装饰者模式的忠实粉丝)。

回答by jpierson

If your objects will need to be serialized in some way that retains references such as with NetDataContractSerializeror perhaps protobufevents 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.

如果您的对象需要以某种方式序列化以保留引用,例如使用NetDataContractSerializerprotobuf事件将无法跨越序列化边界。由于观察者模式仅依赖于对象引用,因此如果需要,它可以毫无问题地使用这种类型的序列化。

Ex. You have a bunch of business objects that link to each other bidirectionally that you need to pass to a web service.

前任。您有一堆相互链接的业务对象,您需要将它们传递给 Web 服务。