在C#中提升事件:表现和优雅

时间:2012-03-19 07:45:07

标签: c# .net events delegates lambda

习惯于VB.NET,我习惯于“只是举起事件”。当然,自定义事件有所不同,但有“常规”事件 - 我不需要在提升之前检查代理是否为Nothing

使用C#,我发现自己重复这种模式:

if (myHandler != null) 
{
    myHandler(this, new EventArgs());
}

我认为以下模式可能会更优雅:

  1. myHandler初始化为空lambda:myHandler = (sender, e) => { };
  2. myHandler预计永远不会为null,所以提升只会变成:myHandler(this, new EventArgs());
  3. 这种模式的性能是否比前一种更高或更低? 我还应该考虑其他重要因素吗?

4 个答案:

答案 0 :(得分:5)

这在建筑中是额外的事情,但它不会是巨大的开销。需要注意的一点是,某些序列化框架(如DataContractSerializer(WCF)不会运行构造函数或字段初始值设定项,因此它毕竟不是非null。就个人而言,如果您的大部分活动都是EventHandler,我可能会尝试使用扩展方法:

public static void SafeInvoke(this EventHandler handler, object sender) {
    if (handler != null) handler(sender, EventArgs.Empty);
}

然后:

SomeEvent.SafeInvoke(this);

虽然坦率地说,我很少使用null-check来解决问题; p

另一个缺点是,如果您有足够的事件,这是一个问题,您可能应该使用EventHandlerList - 而这种方法不适用于EventHandlerList

答案 1 :(得分:2)

通常的做法是使用受保护的虚方法OnEventName,您可以在其中检查事件是否为null并将其提升:

protected virtual void OnEventName(parameters)
{
    if (EventName != null)
        EventName(this, new EventNameEventArgs(parameters);
}

这允许您将所有事件引发代码放在一个位置(无空检查重复),并在以后根据需要覆盖它。因此,对于每个事件添加一个虚拟事件处理程序而不是每个事件进行一次空检查,我看不出任何好处。

BTW更短的添加虚拟处理程序的方法是myHandler = delegate {};

答案 2 :(得分:1)

不要认为第1和第2个案例之间存在显着差异,至少不要考虑它们。在过于频繁使用代理时,缺少if (myHandler != null)可能会让您获得一些性能优势。因此,如果您确定处理程序从不 null,请摆脱该控制,基本上就完成了。

答案 3 :(得分:1)

我认为两种方法之间的性能差异不够大是相关的。如果有的话,我认为空检查比通过委托调用方法更便宜,即使它是无操作。

在讨论优雅时,应该指出VB.NET is automatically expanded by the compiler中的RaiseEvent关键字与您必须在C#中编写的完全相同的构造:

If (Not MyEvent Is Nothing) Then
  MyEvent.Invoke(New EventArgs())
End If

如果您想避免在整个代码中重复该构造,可以将其封装在几个扩展方法中:

public static void RaiseEvent(this EventHandler source, object sender)
{
    if (source != null)
    {
        source.Invoke(sender, new EventArgs());
    }
}

public static void RaiseEvent<T>(this EventHandler<T> source, object sender, T eventArgs)
    where T : EventArgs
{
    if (source != null)
    {
        source.Invoke(sender, eventArgs);
    }
}

这样你就可以简单地说:

myEvent.RaiseEvent(this);
myOtherEvent.RaiseEvent(this, new SomeEventArgs());

在语义上等同于VB.NET中使用的样式。