事件订阅者是不是按订阅顺序调用?
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】:一般来说,事件订阅者的行为应该彼此独立。无论是按订阅顺序、反向订阅顺序还是以看似随机的顺序(每次引发事件时任意变化)调用它们都应该没有区别。订阅者不应该关心在他们之前或之后执行的其他订阅者。
然而,在某些情况下,事件可能会在这种排序很重要的上下文中使用。事件处理程序可能会被传递一个可变对象,并被期望利用先前处理程序对该对象的突变。在这种情况下,如果事件的有意义的操作需要它们以特定的顺序执行,并且已经遵守了对订阅者的任何书面要求,那么人们应该期望事件将按照给定的顺序执行。
【讨论】:
以上是关于事件订阅者是不是按订阅顺序调用?的主要内容,如果未能解决你的问题,请参考以下文章