取消订阅处理程序内的事件是否总是安全的?

Tri*_*ibo 8 c# events

让我们说我有这个(不完整的)类,我在其中引发一个事件而没有先将它分配给一个变量以使其成为线程安全的:

public class Test
{
    public event EventHandler SomeEvent;

    void OnSomeEvent(EventArgs e)
    {
        if (SomeEvent != null)
            SomeEvent(this, e);
    }
}
Run Code Online (Sandbox Code Playgroud)

从自身取消订阅事件处理程序是否安全,或者是否存在类似于在枚举时从集合中删除项目时会发生什么问题?

void SomeEventHandler(object sender, EventArgs e)
{
    testInstance.SomeEvent -= SomeEventHandler;
}
Run Code Online (Sandbox Code Playgroud)

usr*_*usr 7

为了澄清另一个答案:

事件基于代表(几乎在所有情况下).代表是不可改变的.这也适用于多播代理.

调用事件时,会加载委托,然后调用委托.如果修改了存储委托的字段,则这不会影响已加载的委托.

因此,从处理程序修改事件是安全的.这些更改不会影响当前正在运行的调用.这是有保证的.

所有这些仅适用于委托支持的事件.C#和CLR支持可以执行任何操作的自定义事件.


Fab*_*lai 5

然而,这是安全的,只要知道这并不能保证其中的代码SomeEventHandler只会执行一次。如果您有多线程代码,则可能会出现竞争条件。

编辑:取消订阅活动将在幕后合并代表以生成代表列表。(很多细节可以在Jon Skeet 本人的文章中找到)

请注意,该事件使用锁来保证委托组合上的线程安全。将代表合并到活动中后,您将获得代表的结果列表。但是,在引发事件时,不能保证将使用最新版本的组合委托。(请参阅线程安全事件)但这与事件从事件内部取消挂钩这一事实无关。

我希望我的编辑能提供足够的澄清:)