在C#中引发事件:性能与优雅

4

作为一个习惯于VB.NET的开发者,我习惯于“仅仅触发事件”。当然,自定义事件会有不同,但是对于“普通”的事件- 在触发之前我不需要检查委托是否为Nothing

而在使用C#时,我发现自己不得不反复使用这个模式:

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

我认为以下模式可能更加优雅:
  1. 用空lambda初始化myHandler:myHandler = (sender,e) => { };
  2. 预期myHandler永远不会为空,因此引发事件将变为:myHandler(this, new EventArgs());
相比之前的模式,这种模式的性能更好还是更差? 是否应考虑其他重要因素?

2
请参考这个非常相似的问题的答案:https://dev59.com/VXVC5IYBdhLWcg3w1E1q - Andrew Kennan
4个回答

5

在构建过程中,可能会出现一些额外的情况,但这不会造成很大的负担。需要注意的是,一些序列化框架(如DataContractSerializer(WCF))不会运行构造函数或字段初始化器,因此它可能仍然为空值。个人而言,如果您的大多数事件都是EventHandler,我可能会尝试使用扩展方法:

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

那么:

SomeEvent.SafeInvoke(this);

尽管坦率地说,我很少遇到只使用空值检查就能解决的问题。另一个缺点是,如果你有足够多的事件,这将成为一个问题,那么你应该使用 EventHandlerList,这种方法将无法使用 EventHandlerList

2

通常的做法是使用受保护的虚方法OnEventName,在这里您可以检查事件是否为null并触发它:

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

这样做可以将所有事件触发代码放在一个地方(无需重复进行null检查),并在需要时进行覆盖。因此,我认为每个事件添加一个虚拟事件处理程序而不是每个事件添加一个null检查没有任何好处。

顺便提一下,更短的添加虚拟处理程序的方法是myHandler = delegate {};


1

我认为这两种方法之间的性能差异不足以成为相关因素。如果有什么区别,我会认为空值检查比通过委托调用方法更便宜,即使它是一个无操作。

谈到优雅性时,应该指出,在VB.NET中,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中使用的样式在语义上是等效的。


1

我认为第一种和第二种情况并没有显著的区别,至少不足以考虑它们。在委托的使用过于频繁时,缺乏if (myHandler != null)可能会给你带来一些性能上的好处。因此,如果你确定处理程序永远不会是null,那么就摆脱这个控制,基本上你就完成了。


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