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

Tom*_*rle 68 c# clr events delegates

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

if ( MyEvent != null ) {
  MyEvent( param1, param2 );
}
Run Code Online (Sandbox Code Playgroud)

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

MyEvent( param1, param2 );
Run Code Online (Sandbox Code Playgroud)

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

void Initialize() {
  MyEvent += new MyEvent( (p1,p2) => { } );
}
Run Code Online (Sandbox Code Playgroud)

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

Din*_*nah 145

我在另一个帖子上看到了这个并且无耻地偷了它并且在我的大部分代码中使用它从那以后:

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");
}
Run Code Online (Sandbox Code Playgroud)

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

(编辑:我从这个帖子中获得了C#的隐藏功能?)

  • -1有两个原因:1)这种技术会产生运行时性能和内存开销; 2)这种技术容易出错,特别是与下面描述的扩展方法相比.单独查看调用站点不足以确定此方法的正确性,但扩展方法适用于所有事件,无论事件是否使用空委托进行初始化. (5认同)
  • 添加空委托,就在那里!这甚至比我希望的更好.谢谢!我现在要阅读"隐藏的功能"帖子. (3认同)
  • 大声笑.我想我只是从我的代码库中删除了大约300行! (3认同)

TcK*_*cKs 58

符号:

if ( MyEvent != null ) {
  MyEvent( param1, param2 );
}
Run Code Online (Sandbox Code Playgroud)

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

EventHandler handler = this.MyEvent;
if ( null != handler ) { handler( param1, param2 ); }
Run Code Online (Sandbox Code Playgroud)

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

static void RaiseEvent( EventHandler handler, object sender, EventArgs e ) {
    if ( null != handler ) { handler( sender, e ); }
}
Run Code Online (Sandbox Code Playgroud)

然后打电话:

RaiseEvent( MyEvent, param1, param2 );
Run Code Online (Sandbox Code Playgroud)

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

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

然后打电话:

MyEvent.Raise( param1, param2 );
Run Code Online (Sandbox Code Playgroud)

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

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

  • 使用扩展方法是一个很好的解决方案.当提出初始化空代表的概念时,我感到畏缩. (4认同)
  • 哇,我第一次看到 `=delegate {}` 时觉得它很方便。不过,这 +1 很棒。事后看来如此明显,该死的:) (2认同)

gan*_*ter 7

在C#6.0中,由于有条件的null运算符,因此无需使用任何这些长度来执行null检查 ?.

该文档解释说,调用MyEvent?.Invoke(...)会将事件复制到一个临时变量,执行null检查,如果不为null,则调用Invoke该临时副本。从某种意义上说,这不一定是线程安全的,因为有人可以在将副本复制到临时变量之后添加一个新事件,该事件不会被调用。它确实保证您不会调用Invokenull。

简而言之:

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

public void DoSomething() {
    Click?.Invoke(this, "foo");
}
Run Code Online (Sandbox Code Playgroud)

  • 我还要补充一点,根据我的测试,带有空委托的 `MyEvent?.Invoke(...)` 和 `MyEvent(...)` 的性能差异是显着的:`.?` 大约是比空委托方法快约 40%。有关我用来测试此问题的代码,请参阅 https://gist.github.com/didii/c4e8ef021fb8b9fca7898d71eb0de79a。 (3认同)

lep*_*pie 6

你可以这样写:

MyEvent += delegate { };
Run Code Online (Sandbox Code Playgroud)

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


vke*_*man 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);
  }
}
Run Code Online (Sandbox Code Playgroud)