自动创建空的C#事件处理程序

时间:2008-12-04 13:41:29

标签: c# events delegates clr

无法在没有附加处理程序的C#中触发事件。因此,在每次调用之前,有必要检查事件是否为空。

if ( MyEvent != null ) {
  MyEvent( param1, param2 );
}

我想尽可能保持我的代码干净并摆脱那些空检查。我不认为这会影响性能,至少在我的情况下不会。

MyEvent( param1, param2 );

现在我通过手动为每个事件添加一个空的内联处理程序来解决这个问题。这很容易出错,因为我需要记住这样做等等。

void Initialize() {
  MyEvent += new MyEvent( (p1,p2) => { } );
}

有没有办法使用反射和一些CLR魔法自动为给定类的所有事件生成空处理程序?

8 个答案:

答案 0 :(得分:141)

我在另一篇文章中看到了这一点并且无耻地偷了它并且从那以后在我的大部分代码中使用它:

public delegate void MyClickHandler(object sender, string myValue);
public event MyClickHandler Click = delegate {}; // add empty delegate!

//Let you do this:
public void DoSomething() {
    Click(this, "foo");
}

//Instead of this:
public void DoSomething() {
    if (Click != null) // Unnecessary!
        Click(this, "foo");
}

*如果有人知道这种技术的起源,请在评论中发布。我确实相信来源得到应有的信任。

修改:我是从这篇文章Hidden Features of C#?获得的)

答案 1 :(得分:58)

符号:

if ( MyEvent != null ) {
  MyEvent( param1, param2 );
}

不是线程安全的。你应该这样做:

EventHandler handler = this.MyEvent;
if ( null != handler ) { handler( param1, param2 ); }

我明白,这是一个麻烦,所以你可以做帮助方法:

static void RaiseEvent( EventHandler handler, object sender, EventArgs e ) {
    if ( null != handler ) { handler( sender, e ); }
}

然后致电:

RaiseEvent( MyEvent, param1, param2 );

如果您使用的是C#3.0,则可以将辅助方法声明为扩展方法:

static void Raise( this EventHandler handler, object sender, EventArgs e ) {
    if ( null != handler ) { handler( sender, e ); }
}

然后致电:

MyEvent.Raise( param1, param2 );

您还可以为其他事件处理程序创建下一个扩展/帮助程序方法。例如:

static void Raise<TEventArgs>( this EventHandler<TEventArgs> handler,
    object sender, TEventArgs e ) where TEventArgs : EventArgs
{
    if ( null != handler ) { handler( sender, e ); }
}

答案 2 :(得分:6)

你可以写为:

MyEvent += delegate { };

我不确定你想做什么是正确的。

答案 3 :(得分:6)

在C#6.0中,由于条件空操作符?.

,无需使用任何这些长度进行空检查。

The docs解释调用MyEvent?.Invoke(...)将事件复制到临时变量,执行空检查,如果不为null,则在临时副本上调用Invoke。在任何意义上,这都不一定是线程安全的,因为有人可能在复制到临时变量之后添加了一个新事件,这个事件不会被调用。它确实保证你不会在null上调用Invoke

简而言之:

public delegate void MyClickHandler(object sender, string myValue);
public event MyClickHandler Click;

public void DoSomething() {
    Click?.Invoke(this, "foo");
}

答案 4 :(得分:5)

对于不同的事件处理程序,您不需要多种扩展方法,只需要一个:

public static class EventHandlerExtensions {
  public static void Raise<T>(this EventHandler<T> handler, object sender, T args) where T : EventArgs {
    if (handler != null) handler(sender, args);
  }
}

答案 5 :(得分:2)

这是一个坏主意,因为现在消耗该事件的代码期望具有该事件的对象默认使用动作进行编码。如果您的代码永远不会被其他任何人在其他地方使用,那么我想您可以逃脱它。

答案 6 :(得分:1)

不幸的是,C#事件声明包括许多众所周知的安全问题和效率低下。 I designed a number of extension methods on delegates to invoke them safely, and to register/unregister delegates in a thread-safe manner

您的旧事件提升代码:

if (someDelegate != null) someDelegate(x, y, z);

您的新代码:

someDelegate.Raise(x, y, z);

您的旧活动注册码:

event Action fooEvent;
...
lock (someDummyObject) fooEvent += newHandler;

您的新代码:

Action fooEvent;
...
Events.Add(ref fooEvent, newHandler);

不需要锁定,没有用于锁定事件的编译器插入的虚拟对象。

答案 7 :(得分:-1)