在事件处理程序中使用null check

Gur*_*epS 27 c#

检查事件处理程序是否为null时,是否基于每个线程完成?

确保有人正在听这个事件就像这样:

EventSeven += new DivBySevenHandler(dbsl.ShowOnScreen);
Run Code Online (Sandbox Code Playgroud)

如果我在上面检查null的模式之后添加代码,那么为什么我需要一个空检查(从这个站点获取的代码).我错过了什么?

此外,事件和GC的规则是什么?

Mar*_*ell 51

问题是如果没有人订阅该事件,它就是null.并且你无法调用null.跳过三种方法:

  • 检查为null(见下文)
  • 添加"无所事事"处理程序: public event EventHandler MyEvent = delegate {};
  • 使用扩展方法(见下文)

当空检查,是线程安全的,你必须在理论上首先捕捉委托引用(如果它的检查和调用不同的):

protected virtual void OnMyEvent() {
    EventHandler handler = MyEvent;
    if(handler != null) handler(this, EventArgs.Empty);
}
Run Code Online (Sandbox Code Playgroud)

扩展方法具有不寻常的属性,它们可以在null实例上调用...

    public static void SafeInvoke(this EventHandler handler, object sender)
    {
        if (handler != null) handler(sender, EventArgs.Empty);
    }
    public static void SafeInvoke<T>(this EventHandler<T> handler,
        object sender, T args) where T : EventArgs
    {
        if (handler != null) handler(sender, args);
    }
Run Code Online (Sandbox Code Playgroud)

然后你可以打电话:

MyEvent.SafeInvoke(this);
Run Code Online (Sandbox Code Playgroud)

并且它是零安全的(通过检查)和线程安全的(通过仅读取一次引用).

  • 喜欢通过堆栈上的隐含副本的线程安全性 (2认同)

Jon*_*eet 48

我真的不清楚你的意思我害怕,但如果委托的可能性为null,你需要在每个线程上单独检查.通常你会这样做:

public void OnSeven()
{
    DivBySevenHandler handler = EventSeven;
    if (handler != null)
    {
        handler(...);
    }
}
Run Code Online (Sandbox Code Playgroud)

这可以确保即使您EventSeven在课程期间的更改OnSeven()无法获得NullReferenceException.

但是你是对的,如果你肯定有一个订阅的处理程序,你不需要空检查.这可以通过"无操作"处理程序在C#2中轻松完成:

public event DivBySevenHandler EventSeven = delegate {};
Run Code Online (Sandbox Code Playgroud)

另一方面,如果您可以从各种线程获得订阅,您可能需要某种锁定以确保您拥有"最新"处理程序集.我在我的线程教程中有一个例子可以提供帮助 - 尽管我通常建议尽量避免要求它.

在垃圾收集方面,事件发布者最终会引用事件订阅者(即处理程序的目标).如果发布者的寿命比订阅者长,这只是一个问题.

  • @HosseinNarimaniRad:它和答案中的例子一样安全,是的.C#的内存模型并不像它可能那样清晰,但基本上,我认为它是安全的. (2认同)

unk*_*656 30

我想附加一些关于C#6.0-Syntax的简短信息:

现在可以替换这个:

var handler = EventSeven;

if (handler != null)
    handler.Invoke(this, EventArgs.Empty);
Run Code Online (Sandbox Code Playgroud)

有了这个:

handler?.Invoke(this, EventArgs.Empty);
Run Code Online (Sandbox Code Playgroud)


将它与表达式成员组合,您可以缩短以下代码:

protected virtual void OnMyEvent()
{
    EventHandler handler = MyEvent;
    handler?.Invoke(this, EventArgs.Empty);
}
Run Code Online (Sandbox Code Playgroud)

下到单线:

protected virtual void OnMyEvent() => MyEvent?.Invoke(this, EventArgs.Empty);
Run Code Online (Sandbox Code Playgroud)


有关空条件运算符的更多信息,请参阅MSDN.
请参阅此博客关于表达式成员的信息

  • 事实上,似乎是的。https://codeblog.jonskeet.uk/2015/01/30/clean-event-handlers-inspiration-with-c-6/ (3认同)