为什么“公共事件EventHandler cccc”将为空?

问题描述 投票:0回答:5

为什么“公共事件 EventHandler cccc”为空?

我有一堂课

public class Builder
{
    public event EventHandler StartedWorking;

    public Builder()
    { 
        // Constructor does some stuff
    }

    public void Start()
    {
       StartedWorking(this, eventargobject); //StartedWorking is null --
    }
}   

这看起来很简单,而且是我一直在做的事情?我是否遗漏了一些明显的东西,或者是否有什么东西可能导致这种情况?

编辑:

这是否意味着,如果我触发一个未在客户端类中订阅的事件,我必须检查它是否不为空?

编辑2:

我想我从来没有遇到过没有订阅的事件,因此从来没有遇到过这种情况—— 你每天学习新的东西 抱歉这个看似愚蠢的问题......

c# .net events
5个回答
16
投票

除非有人订阅了该事件,否则事件处理程序将为空。一旦委托订阅了事件,它就不再为空。

这就是为什么总是建议使用以下形式来引发事件:

public void Start()
{
    var handler = this.StartedWorking;
    if (handler != null)
    {
         handler(this, eventArgObject);
    }
}

如果没有订阅者,这可以保护您免受空异常的影响。


3
投票

正如其他人已经说过的,它是空的,因为没有订阅者。

回答您的编辑:是的,您应该始终在触发事件之前检查事件是否为空。但是,如果您只是执行简单的

if(StartedWorking != null){...}
操作,则会面临竞争条件的风险,因为订阅者可能会在空检查之后但在触发事件之前取消订阅。因此,在检查事件是否为空时,您应该始终使用此模式:

protected void OnStartedWorking()
{
    EventHandler localEvent = StartedWorking
    if(localEvent != null)
    {
        localEvent(this, EventArgs.Empty);
    }
}

这可以通过首先获取事件的副本来防止竞争条件,以便订阅列表在复制时固定。

MSDN 上有关于发布事件的更多信息:如何发布符合 .NET Framework 指南的事件

(这有效,因为在 .net 中 MultiCastDelegate 类是不可变的,因此任何更改事件订阅者列表的尝试都不会影响您所做的副本)


0
投票

如果您尚未将任何事件订阅者连接到 StartedWorking 事件,则该事件将为空。这就是 .NET 事件的工作原理。

本文除其他外,演示了您应该在调用事件之前检查 null 。 这个其他问题和答案演示了如何以避免空检查的方式创建事件(基本上通过始终添加空处理程序)。


0
投票

不需要分配功能吗?

StartedWorking += new EventHandler(afunction);

void afunction(object sender, EventArgs e)
{
   DoSomething();
}

0
投票

接受的答案是正确的,但从 c# 6.0 开始,有一个称为 空条件运算符 的功能。有了这个功能,通话就可以简化为

public void Start()
{
    var handler = this.StartedWorking;
    if (handler != null)
    {
        handler(this, eventArgObject);
    }
}

public void Start()
{
    var handler = this.StartedWorking;
    handler?.Invoke(this, eventArgObject);
}

.

这会产生更清晰的代码,并且与接受的答案具有完全相同的行为。

© www.soinside.com 2019 - 2024. All rights reserved.