这是一种更好的方法来启动/调用事件而无需在C#中进行空检查吗?

Bea*_*ker 12 .net c# resharper events event-handling

我见过的大多数代码都使用以下方式来声明和调用事件触发:

public class MyExample
{
    public event Action MyEvent; // could be an event EventHandler<EventArgs>, too

    private void OnMyEvent()
    {
        var handler = this.MyEvent; // copy before access (to aviod race cond.)
        if (handler != null)
        {
            handler();
        }
    }

    public void DoSomeThingsAndFireEvent() 
    {
        // ... doing some things here
        OnMyEvent();
    }
 }
Run Code Online (Sandbox Code Playgroud)

甚至ReSharper也会按照上面提到的方式生成一个调用方法.

为什么不这样做:

public class MyExample
{
    public event Action MyEvent = delegate {}; // init here, so it's never null

    public void DoSomeThingsAndFireEvent() 
    {
        // ... doing some things here
        OnMyEvent(); // save to call directly because this can't be null
    }
 }
Run Code Online (Sandbox Code Playgroud)

任何人都可以解释为什么不这样做的原因?(亲与利弊)

Eri*_*ert 16

优点和缺点是:

  • 空检查非常便宜; 我们正在谈论十亿分之一秒.在对象的整个生命周期中分配一个委托然后垃圾收集失败可能需要花费几百万分之一秒.另外,你消耗了几十个字节的内存.此外,每次事件触发时,您都会对不执行任何操作的方法进行不必要的调用,从而消耗更多的微秒.如果你是那种关心百万分之一秒和几十个字节的人,那么这可能是一个有意义的差异; 对于绝大多数情况,它不会.

  • 您必须记住始终创建空委托.这真的比记住检查null更容易吗?

  • 这两种模式都不会使事件线程安全.事件处理程序在一个线程上被触发同时在另一个线程上被移除时,两个模式仍然是完全可能的,这意味着它们会竞争.如果您的处理程序删除代码破坏了处理程序所需的状态,那么当另一个线程正在运行处理程序时,一个线程可能正在销毁该状态.不要以为只检查null或分配空处理程序会神奇地消除竞争条件.它只消除导致取消引用null的竞争条件.

  • 在没有使用处理程序的情况下,不仅添加空处理程序浪费内存,更重要的是它使得添加和删除第一个真正的处理程序*更加昂贵.将第一个处理程序添加到事件非常便宜:只需用处理程序替换null即可.从事件中删除唯一的处理程序很便宜:将其替换为null.相比之下,所有其他情况都要贵得多. (3认同)

sco*_*kel 6

这肯定是一种风格的东西; 但是,我认为大多数开发人员在进行空检查时都会考虑安全性. 在这个世界上没有任何东西可以保证一个bug不会蔓延到系统中,并且无需进行空检查.


Dan*_*zey 5

可以仍然为空.考虑:

    var x = new MyExample();
    x.MyEvent += SomeHandler;

    // ... later, when the above code is disposed of

    x.MyEvent -= SomeHandler;
Run Code Online (Sandbox Code Playgroud)

编辑:实际上,我把它拿回来.测试过此后,如果您使用匿名委托设置处理程序,则看起来不能清除它,因此您可以保存空检查.

我不确定这是否是可靠的行为,或者只是语言实现的工件......