触发事件的潜在缺点?

时间:2013-11-11 17:46:57

标签: c# events race-condition

Microsoft's tutorial on events显示如何在触发event之前检查null

protected virtual void OnChanged(EventArgs e) 
{
    if (Changed != null)
    {   // Potential Race-condition at this point!
        Changed(this, e);
    }
}

但这会留下一个竞争条件,详见Eric Lippert's blog,他写道,事件应该通过本地事件触发以避免竞争条件:

protected virtual void OnChanged(EventArgs e) 
{
    ChangedEventHandler temp = Changed;  // Local copy of the EventHandler
    if (temp != null)
    {                                    // Race condition avoided by local variable.
        temp(this, e);
    }
}

虽然这样可行,但它让许多错误的开发人员感到困惑,并且不会把它放在本地范围的事件中。


来自DailyCoding的另一个解决方案是始终将事件初始化为一个空处理程序,因此永远不需要进行空检查:

// Set with empty delegate, will never be null
public event ChangedEventHandler Changed = delegate { };

protected virtual void OnChanged(EventArgs e) 
{
    // Neither Null-check nor local variable is needed; just trigger the event.
    Changed(this, e);
}

这个很有道理,很简单 但是,由于我看到这种技术很少在网上提及,我认为必须有一个原因 使用像这样的空委托初始化事件是否有缺点?

3 个答案:

答案 0 :(得分:1)

  • 如果事件是空的,那会有轻微的性能影响
  • 如果您曾在课堂上写过Changed = null,那么它将会中断。

答案 1 :(得分:1)

你会看到一个绝对微小的性能损失,但在更高级的情况下会出现问题,例如序列化和反序列化类可能导致你丢失假事件处理程序,并且缺少空检查然后抛出异常。

答案 2 :(得分:0)

在你发布的Eric Lippert的博客文章中,他说:

  

还有其他方法可以解决这个问题;例如,初始化   具有永不删除的空操作的处理程序。但做一个   null check是标准模式。

但在此之前他还说:

  

删除呼叫站点周围的代码[空检查]不会减少代码[...]中的竞争条件数。 更糟糕的是,这样做会使竞争条件更难以被发现   缩小可以在不消除比赛的情况下进行比赛的窗口   它

这是因为正如他所描述的那样,它仍然可能发生

  

在委托值[到堆栈]的推送和调用它[...]的调用之间

所以基本上,如果你使用一个空的处理程序,你会遇到一些性能损失(这似乎是这里的共识)。所以你获得的是可读性,但最重要的是:奇怪的行为将更加明显。 (我从性能降低中推断出这一点 - >花费更长时间 - >更可能调用过时的处理程序) 所以,如果你完全意识到这些事情会发生,并且空检查不会打扰你,那就去吧。或者不要,如果你不想这样做。