简体   繁体   English

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

[英]Raising Events in C#: performance and elegance

Being accustomed to VB.NET, I'm used to "just raise events". 习惯于VB.NET,我习惯于“只是举起事件”。 Sure, custom events differ, but with "regular" events - I don't need to check if the delegate is Nothing before raising. 当然,自定义事件有所不同,但有“常规”事件 - 我不需要在提升之前检查代表是否为Nothing

With C#, I find myself repeating this pattern: 使用C#,我发现自己重复这种模式:

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

I was thinking that the following pattern might prove more elegant: 我以为以下模式可能会更优雅:

  1. myHandler is initialized with an empty lambda: myHandler = (sender, e) => { }; myHandler初始化为空lambda: myHandler = (sender, e) => { };
  2. myHandler is expected never to be null, so raising would simply become: myHandler(this, new EventArgs()); myHandler预计永远不会为null,所以提升只会变成: myHandler(this, new EventArgs());

Would this pattern be more or less performant than the previous one? 这种模式会比前一种模式更多或更少吗? Are there other significant considerations I should take into account? 我还应该考虑其他重要因素吗?

It is something extra to happen in construction, but it won't be huge overhead. 这在建筑中是额外的事情,但它不会是巨大的开销。 One thing to watch is that some serialization frameworks like DataContractSerializer (WCF) don't run constructors or field initializers, so it may not be non-null after all. 需要注意的一点是,某些序列化框架(如DataContractSerializer (WCF))不运行构造函数或字段初始值设定项,因此它可能不是非null。 Personally, if the majority of your events are EventHandler , I might be tempted to use an extension method: 就个人而言,如果您的大多数事件都是EventHandler ,我可能会想要使用扩展方法:

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

then: 然后:

SomeEvent.SafeInvoke(this);

although be frank, I rarely have a problem with simply using the null-check ;p 虽然坦率地说,我很少使用null-check来解决问题; p

Another downside is that if you have enough events that this is an issue, you should probably be using EventHandlerList instead - and this approach won't work with EventHandlerList . 另一个缺点是,如果你有足够的事件,这是一个问题,你可能应该使用EventHandlerList - 而这种方法不适用于EventHandlerList

Common practice is to use protected virtual methods OnEventName where you can check if event is null and raise it: 通常的做法是使用受保护的虚方法OnEventName,您可以在其中检查事件是否为null并将其提升:

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

This allows you to have all event-raising code in one place (no null check duplication) and override it later if needed. 这允许您将所有事件引发代码放在一个位置(无空检查重复),并在以后根据需要覆盖它。 So I don't see any benefit of adding one dummy event handler per event over having one null check per event. 因此,对于每个事件添加一个虚拟事件处理程序而不是每个事件进行一次空检查,我看不出任何好处。

BTW shorter way to add dummy handler is myHandler = delegate {}; BTW更短的添加虚处理程序的方法是myHandler = delegate {};

Don't think there is significative difference between 1st and 2nd case provided, at least not so much to consider them. 不要认为提供的第一和第二种情况之间存在显着差异,至少不要考虑它们。 On too frequent use of delegates lack of if (myHandler != null) could, by the way, gane you some performance benefits. 过于频繁地使用委托,缺少if (myHandler != null)顺便说一句,可以带来一些性能上的好处。 So if you're sure the handler is never null , get rid of that control and basically you done. 因此,如果您确定处理程序永远不会为 null ,那么摆脱控制权基本上就完成了。

I think that the performance difference between the two approaches isn't big enough to be relevant. 我认为两种方法之间的性能差异不够大 ,无法相关。 If anything, I would argue that a null-check is cheaper than invoking a method through a delegate, even if it's a no-op. 如果有的话,我会争辩说,null检查比通过委托调用方法更便宜 ,即使它是无操作。

When talking about elegance , it should be pointed out that the RaiseEvent keyword in VB.NET is automatically expanded by the compiler to exact same construct that you have to write yourself in C#: 在谈论优雅时 ,应该指出VB.NET中的RaiseEvent关键字会被编译器自动扩展为完全相同的构造,您必须在C#中自己编写:

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

If you want to avoid repeating that construct throughout your code, you could encapsulate it in a couple of extension methods: 如果您想避免在整个代码中重复该构造,可以将其封装在几个扩展方法中:

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);
    }
}

That way you can simply say: 那样你就可以简单地说:

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

which is semantically equivalent to the style used in VB.NET. 它在语义上等同于VB.NET中使用的样式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM