委托 vs 观察者模式

8
有没有关于何时应该使用委托进行间接关联和使用观察者的指导方针?
在C#中,您可以使用委托进行简单的回调。我想函数指针和成员函数指针也可以被认为是委托(我对吗?)。
我意识到,如果要使用观察者,您需要创建一个接口并实现它,因此它更具有强类型和更正式的关系。对于委托,只要函数签名和可访问性匹配,就可以“连接”它们。
委托是否使观察者模式无效?如何决定使用委托还是观察者模式?

2
顺便说一句:在.NET 4中,这些接口已经存在了:IObservable<T>IObserver<T>。而且你可以使用反应式扩展轻松地组合可观察对象和观察者。 - R. Martinho Fernandes
请参见 https://dev59.com/fHRB5IYBdhLWcg3wr48U。 - jpierson
3个回答

9

观察者模式已经以事件的形式为您实现。

事件的优点是可以有多个订阅者,而使用委托只能有一个。 事件更适用于公共接口和您无法完全控制谁想要被通知某些事情发生的场景。实际上,事件只是自动管理的委托列表。您需要看看在您的情况下哪种更有意义。

编辑: 正如评论者Rabbi提到的那样, 上面并不完全正确,因为任何委托都可以成为多路广播委托。事件修饰符的目的是使委托只能在定义它的类内部调用。这对于确保公共接口的封装非常有用。


一个事件只是一种特殊类型的委托。委托不是可以有多个订阅者吗?我认为,事件是只有运算符'+='和'-='的委托,而委托也可以被赋值('='),因此在使用委托而不是事件时,您可以覆盖所有订阅者。我的理解正确吗? - Rodi
C#中的事件概念对于委托而言,就像属性对于字段一样。您可以更改添加/删除操作所执行的操作,参见此处的示例2。当您不这样做时,它会在幕后为您生成一个多路广播委托 - Alex J
我想说的是,事件只使用运算符 +=-=,即使您可以通过使用类似属性的 addremove 结构来更改这些运算符的行为。不能在事件上执行赋值(= 运算符)。我可能错了,因为我太懒了,没有尝试过。 - Rodi
6
这个答案完全是错误的,并在传播互联网上的误解和混淆。它也完全与问题无关。任何委托都可以是多播委托并指向多个订阅者。事件修饰符的作用是限制调用委托函数的对象,不能是定义类以外的其他对象。一个更相关的答案且不会一开始就明显错误,是下面jpierson的答案。 - Rabbi
“限制从定义类以外的任何对象调用委托函数。”听起来像是“封装”。这是正确的吗? - Ömer Faruk Almalı
新的事件链接(由于队列已满,我无法编辑答案)。 - Vanity Slug

5
观察者模式的一个优点是,如果您有许多事件通常由感兴趣的方订阅,则将单个对象传递到方法中以订阅事件比逐个订阅每个事件要容易得多。由于 C# 没有指定匿名类的接口和方法(如同 Java),因此实现观察者模式变得更加费力,所以大多数人仍然选择使用事件。
传统观察者模式的另一个好处是,在需要对订阅者进行询问时,它处理得更好。我曾在对象通过 Web 服务边界传递并存在委托问题的情况下遇到过这种需求,而观察者模式只是对另一个对象的引用,因此只要序列化保持对象图中引用的完整性(例如 NetDataContractSerializer),它就可以正常工作。在这些情况下,可以根据所引用的订阅者是否也在同一对象图中来区分应该在服务边界之前删除的订阅者。

2
委托可以用于实现观察者模式,类似于事件。如果不使用事件,可以参考这里:http://www.dofactory.com/Patterns/PatternObserver.aspx。如果您愿意,将其重构为使用委托不需要太多的工作。我能想到唯一的优点是在所有实现中保持成员名称的一致性,可以通过实现接口来实现。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接