74

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

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

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

MyEvent( param1, param2 );

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

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

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

4

8 回答 8

160

我在另一篇文章中看到了这个,然后无耻地窃取了它并在我的大部分代码中使用它:

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#? 中得到它?

于 2008-12-04T13:44:15.223 回答
61

符号:

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 ); }
}
于 2008-12-04T13:53:10.557 回答
10

在 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");
}
于 2017-06-09T08:19:43.243 回答
6

你可以写成:

MyEvent += delegate { };

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

于 2008-12-04T13:44:39.920 回答
6

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

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);
  }
}
于 2009-03-31T15:11:25.390 回答
2

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

于 2008-12-04T13:53:46.170 回答
1

不幸的是,C# 事件声明包含许多众所周知的安全问题和低效率。我在委托上设计了许多扩展方法来安全地调用它们,并以线程安全的方式注册/注销委托

您的旧事件引发代码:

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

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

于 2011-01-28T02:00:11.863 回答
-1

您可以使用 PostSharp 在构建时添加这个魔法。这是最好的方法。

于 2010-07-23T20:44:59.900 回答