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

C# 取消订阅处理程序中的事件是否总是安全的?,c#,events,C#,Events,假设我有一个(不完整的)类,在这个类中,我引发了一个事件,而没有首先将其分配给变量以使其线程安全: public class Test { public event EventHandler SomeEvent; void OnSomeEvent(EventArgs e) { if (SomeEvent != null) SomeEvent(this, e); } } 取消订阅事件处理程序本身是否安全,或者是否存在类似于

假设我有一个(不完整的)类,在这个类中,我引发了一个事件,而没有首先将其分配给变量以使其线程安全:

public class Test
{
    public event EventHandler SomeEvent;

    void OnSomeEvent(EventArgs e)
    {
        if (SomeEvent != null)
            SomeEvent(this, e);
    }
}
取消订阅事件处理程序本身是否安全,或者是否存在类似于在枚举集合时从集合中移除项时发生的问题

void SomeEventHandler(object sender, EventArgs e)
{
    testInstance.SomeEvent -= SomeEventHandler;
}

但是,这是安全的,只需知道它不能保证
SomeEventHandler
中的代码只执行一次。如果您有多线程代码,可能会出现争用情况

编辑: 取消订阅活动将在幕后合并代表以生成代表列表。(有关详细信息,请访问)

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


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

来澄清另一个答案:

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

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

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


所有这些仅适用于由委托支持的事件。C#和CLR支持可以做任何事情的自定义事件。

什么是
testInstance
?大概它与
sender
@Tigran的
Test
实例相同,正如BoltClock所说,它与
(Test)sender
((Test)sender)相同。SomeEvent-=SomeEventHandler这将允许多个实例。(a,刚才看到戴德也写了+1)是的,它是完全安全的。委托是不可变的,取消订阅事件将生成一个全新的委托对象。现有的处理程序不会受到任何影响,在调用所有处理程序之前,无法对其进行垃圾收集。C#语言通过不允许您编写fire访问器使其更加安全,因此您无法更改事件处理程序的调用方式。此外,我通常在订阅事件之前从事件中取消挂钩。只是为了验证SomeEventHandler只执行一次。你能提供一些证据或引证来说明为什么这是安全的吗?当然。我刚刚编辑了答案。希望这能有所帮助。是的,我应该从这一点开始:代理是不可变的,这是关键+1.