事件订阅者是不是按订阅顺序调用?

Posted

技术标签:

【中文标题】事件订阅者是不是按订阅顺序调用?【英文标题】:Are event subscribers called in order of subscription?事件订阅者是否按订阅顺序调用? 【发布时间】:2008-12-17 12:29:10 【问题描述】:

假设事件订阅者按订阅顺序调用是否安全? 示例:

void One(object sender, EventArgs e) 
void Two(object sender, EventArgs e) 

event EventHandler foo;

foo += One;
foo += Two;

触发事件时是否总是在 Two() 之前调用 One()?

编辑: 你当然不应该依赖它,我只是在想。这个想法是,多播委托类似于命令模式。所以我只是想知道。通常,您会使用一个保持命令顺序的集合,以便您可以执行撤消/重做/任何操作。

【问题讨论】:

欺骗***.com/questions/264196/… 【参考方案1】:

鉴于该实现,是的,它们将始终按该顺序调用。

如果事件实际上使用了一些奇怪而美妙的方式来处理订阅,它可以做不同的事情 - 但“正常”的实现会做正确的事情。

明确地说,订阅事件处理程序只是意味着调用事件的适当“添加”部分。如果事件通过执行以下操作来处理此问题:

myHandler += value;

被翻译成

myHandler = Delegate.Combine(myHandler, value);

和Delegate.Combine 保证订购。但是,如果您有这样的活动:

private LinkedList<EventHandler> eventHandlers = new LinkedList<EventHandler>;

public event EventHandler Foo

    add
    
        eventHandlers.AddFirst(value);
    
    remove
    
        // do stuff here too
    

然后通过执行以下操作触发事件:

foreach (EventHandler handler in eventHandlers)

    handler(this, EventArgs.Empty);

那么处理程序将以相反的顺序被调用。

总结:对于所有正常的事件,您可以依赖排序。理论上,事件可以为所欲为,但我从未见过保持适当顺序的事件。

【讨论】:

乔恩,你比大多数人都清楚,永远不应该依赖事件处理程序的执行顺序;你为什么要放这张幻灯片?【参考方案2】:

请密切注意 Jon Skeet 给出的警告 - “鉴于该实施......”。换句话说,只要进行最轻微的更改(多线程、其他处理程序等),您就有可能失去执行顺序不变性。

不要依赖事件排序。所有事件分派在逻辑上应该是独立的,就好像它们是并行发生的一样。事件是逻辑上独立的动作。

我会更进一步,并断言如果您必须承担事件触发的顺序,那么您存在严重的设计缺陷和/或滥用事件。

【讨论】:

【参考方案3】:

即使以正确的顺序调用它们,我也会尽量不编写依赖于先前已被触发的委托才能正常运行的代码。

如果 Two() 依赖于 One() 正在执行的操作,则要么附加一个以正确顺序调用这两个方法的委托,要么让 Two() 在必要时调用 One()。

【讨论】:

此外,我想说没有这样一种正确的方式来通知订阅者。【参考方案4】:

快速回答是“不关你的事”:)

事件本质上是异步的。这意味着您不是在等待事件被触发或期望它在给定时间发生。它们只是发生,然后你采取行动。想知道“何时”或试图弄清楚“如何”会打破这种本性。

也许在这种情况下,您不需要基于事件的方法来完成工作?

Jon Skeet 所说的对于当前的实现在技术上是正确的,但在 c#8.5 或 VBasic 15.0 中可能不会。依赖实施细节总是弊大于利。

【讨论】:

【参考方案5】:

一般来说,事件订阅者的行为应该彼此独立。无论是按订阅顺序、反向订阅顺序还是以看似随机的顺序(每次引发事件时任意变化)调用它们都应该没有区别。订阅者不应该关心在他们之前或之后执行的其他订阅者。

然而,在某些情况下,事件可能会在这种排序很重要的上下文中使用。事件处理程序可能会被传递一个可变对象,并被期望利用先前处理程序对该对象的突变。在这种情况下,如果事件的有意义的操作需要它们以特定的顺序执行,并且已经遵守了对订阅者的任何书面要求,那么人们应该期望事件将按照给定的顺序执行。

【讨论】:

以上是关于事件订阅者是不是按订阅顺序调用?的主要内容,如果未能解决你的问题,请参考以下文章

RxSwift 共享订阅执行顺序

wcf 调用完成事件未按正确顺序触发

硬核解析 Webpack 事件流核心!

EventBus事件通信框架 ( 发送事件 | 根据事件类型获取订阅者 | 调用订阅方法 )

学习RxJS: 导入

持久的有序消息代理的用例是啥