自动创build空的C#事件处理程序
在C#中没有附加处理程序的事件是不可能的。 所以在每次调用之前,都需要检查事件是否为空。
if ( MyEvent != null ) { MyEvent( param1, param2 ); }
我想保持我的代码尽可能干净,摆脱这些空检查。 我不认为这会影响performance,至less不会影响我的performance。
MyEvent( param1, param2 );
现在我通过手动为每个事件添加一个空的内联处理程序来解决这个问题。 这是错误的容易,因为我需要记住这样做等。
void Initialize() { MyEvent += new MyEvent( (p1,p2) => { } ); }
有没有办法使用reflection和一些CLR魔法自动生成给定类的所有事件的空处理程序?
我在另一篇文章中看到了这一点,并且无耻地窃取了它,并在以后的大部分代码中使用它:
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#? )
符号:
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 );
您也可以为其他事件处理程序创build下一个扩展/帮助程序方法。 例如:
static void Raise<TEventArgs>( this EventHandler<TEventArgs> handler, object sender, TEventArgs e ) where TEventArgs : EventArgs { if ( null != handler ) { handler( sender, e ); } }
你可以写为:
MyEvent += delegate { };
我不确定你想要做的是正确的。
对于不同的事件处理程序,您不需要多个扩展方法,您只需要一个:
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); } }
这是一个坏主意,因为消耗事件的代码现在预期具有事件的对象已经用默认的动作编码。 如果你的代码永远不会被其他人使用,那么我想你可以逃脱。
不幸的是,C#事件声明包含了一些众所周知的安全问题和低效率。 我在委托上devise了许多扩展方法来安全地调用它们,并以线程安全的方式注册/取消注册委托 。
您的旧活动筹码:
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);
不需要locking,没有编译器插入的虚拟对象用于locking事件。
在C#6.0中,没有必要使用任何这些长度来执行空检查,这要归功于条件空操作符?.
该文档解释调用MyEvent?.Invoke(...)
将事件复制到一个临时variables,执行空检查,如果不为空,调用临时副本上的Invoke
。 在任何意义上,这都不一定是线程安全的,因为有些人可能在复制到临时variables后添加了一个新事件,而这个variables不会被调用。 它确实保证你不会在null上调用Invoke
。
简而言之:
public delegate void MyClickHandler(object sender, string myValue); public event MyClickHandler Click; public void DoSomething() { Click?.Invoke(this, "foo"); }
你可以使用PostSharp来构build时间添加这个魔法。 这是最好的方法。