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

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

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

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

MyEvent( param1, param2 );

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

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

是否有一种方法可以使用反射和一些 CLR 魔法为给定类的所有事件自动生成空处理程序?

54754 次浏览

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

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

* 如果有人知道这项技术的来源,请在评论中发表。我真的相信消息来源会得到应有的信任。

(我从这篇文章 C # 的隐藏特性?中得到的)

你可以这样写:

MyEvent += delegate { };

我不确定你想做的是否正确。

注释:

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,可以将 helper 方法声明为扩展方法:

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

这是一个坏主意,因为正在使用事件的代码现在预期带有事件的对象已经在默认情况下用一个操作进行了编码。如果您的代码永远不会被其他任何人在任何地方使用,那么我想您可以不受惩罚。

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

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

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

遗憾的是,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);

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

在 C # 6.0中,由于使用了条件 null 操作符 ?.,因此不需要使用这些长度来执行 null 检查

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

简而言之:

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


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