事件订阅者按照订阅顺序被调用吗?

30

可以安全地假设事件订阅者按订阅顺序被调用吗?
示例:

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

event EventHandler foo;

foo += One;
foo += Two;

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

编辑:
当然你不应该依赖这个,我只是思考。我的想法是,多播委托类似于COMMAND模式。所以我只是在想。通常,您会使用一个集合来为COMMAND保持顺序,以便可以执行撤销/重做/其他操作。


1
如何控制事件处理程序的触发顺序? - Jon B
5个回答

48

考虑到该实现,是的,事件处理程序将始终按照相应顺序调用。

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

需要明确的是,订阅事件处理程序只是调用事件的相应“添加”部分。如果事件通过执行以下操作来处理此操作:

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);
}

然后处理程序将按相反的顺序被调用。

摘要: 对于所有合理的事件,您可以依赖其顺序。理论上,事件可以随意操作,但是我从未见过一个不维护适当顺序的事件。


8
Jon,你比大多数人更清楚地知道不能依赖事件处理程序的执行顺序;为什么你对此置之不理? - Steven A. Lowe

25

请非常注意Jon Skeet所给出的警告 - "鉴于实现情况..."。换句话说,稍作更改(多个线程、其他处理程序等),您就有可能丧失执行次序不变性。

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

我再进一步强调,如果您必须假定事件触发顺序,则存在严重的设计缺陷和/或误用事件。


12

即使它们按照正确的顺序调用,我也会尽量避免编写依赖先前委托已被触发才能正确执行的代码。

如果Two()依赖于One()正在执行的某些操作,则可以附加一个单一的委托,按照正确的顺序调用这两个方法,或者在必要时由Two()调用One()。


此外,我认为没有一种“正确”的方式来通知订阅者。 - Trap

4
快速的回答是“这不关你的事” :)
事件天生就是异步的。这意味着你不需要等待事件被触发或期望它在特定时间发生。它们只会发生,然后你采取行动。想知道“何时”或试图弄清“如何”将会破坏这种本质。
也许在这种情况下,您不需要基于事件的方法来完成任务?
Jon Skeet所说的在当前实现上技术上是正确的,但在c#8.5或VBasic 15.0中可能不再适用。依赖实现细节总会带来更多的伤害而不是好处。

0
一般来说,事件订阅者应该独立运作。无论按照订阅顺序、反向订阅顺序还是以似乎随机变化的顺序触发事件,都不应该对它们造成影响。订阅者不应关心在他们之前或之后执行的其他订阅者。
然而,在某些情况下,事件可能被用于有序处理的上下文中。事件处理程序可以接收可变对象,并期望使用先前处理程序对该对象所做的更改。在这种情况下,如果事件的有意义操作需要按特定顺序执行,并且已遵守了所有记录的订阅者要求,则应该期望按给定的顺序执行事件。

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