事件处理程序触发的方法约定

6
我只是在浏览时看到了这个问题:

Action vs delegate event

nobug的回答包含了以下代码:

protected virtual void OnLeave(EmployeeEventArgs e) {
  var handler = Leave;
  if (handler != null)
    handler(this, e);
}

Resharper还可以在使用“创建引发方法”快速修复时生成类似的代码。
我的问题是,为什么这行代码是必要的?
var handler = Leave;

为什么它比写这个更好呢?
protected virtual void OnLeave(EmployeeEventArgs e) {
  if (Leave != null)
    Leave(this, e);
}
3个回答

12

这么做更好是因为在进行null检查之后到调用之前,Leave有可能变成null(这会导致你的代码抛出NullReferenceException)。因为委托类型是不可变的,如果你先将它赋值给一个变量,这种可能性就会消失;你的本地副本不会受到分配之后对Leave所做任何更改的影响。

但请注意,这种方法也会产生相反的问题;这意味着在事件被解除绑定之后,仍有可能发生事件处理程序被调用的情况(虽然很小,但是存在)。当然,这种情况也应该得到优雅的处理。


5
在多线程应用程序中,如果调用者取消注册事件,则可能会出现空引用异常。将其分配给本地变量可以防止此问题。
很有可能你从未遇到过这种情况(直到它对你产生了最坏的影响)。以下是一种展示问题的方式...
protected virtual void OnLeave(EmployeeEventArgs e) {
  if (Leave != null) //subscriber is registered to the event
  {
    //Subscriber unregisters from event....
    Leave(this, e); //NullReferenceException!
  }
}

是的。在注销事件处理程序后调用它是一个无法修复的竞态条件。空引用异常很容易修复。 - Hans Passant

1
这是一篇关于编程的文章,以下是Eric Lippert的精彩解释:

事件和竞赛


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接