用扩展方法提高C#事件 – 这不好吗?

我们都熟悉C#事件声明的恐怖。 为了确保线程安全, 标准是写这样的东西 :

public event EventHandler SomethingHappened; protected virtual void OnSomethingHappened(EventArgs e) { var handler = SomethingHappened; if (handler != null) handler(this, e); } 

最近在这个板上的其他问题上(现在我找不到),有人指出扩展方法在这种情况下可以很好地使用。 以下是一种方法:

 static public class EventExtensions { static public void RaiseEvent(this EventHandler @event, object sender, EventArgs e) { var handler = @event; if (handler != null) handler(sender, e); } static public void RaiseEvent<T>(this EventHandler<T> @event, object sender, T e) where T : EventArgs { var handler = @event; if (handler != null) handler(sender, e); } } 

有了这些扩展方法,你需要声明和引发一个事件就像这样:

 public event EventHandler SomethingHappened; void SomeMethod() { this.SomethingHappened.RaiseEvent(this, EventArgs.Empty); } 

我的问题:这是一个好主意吗? 我们是否缺less标准的On方法? (我注意到的一件事是,它不适用于具有明确的添加/删除代码的事件。)

它仍然可以使用具有显式添加/删除的事件 – 只需使用委托variables(或者存储委托)而不是事件名称即可。

但是,有一个更简单的方法可以使其成为线程安全的 – 使用no-op处理程序对其进行初始化:

 public event EventHandler SomethingHappened = delegate {}; 

调用额外委托的性能受到的影响是微不足道的,它确实使代码更容易。

顺便说一句,在你的扩展方法中,你不需要额外的局部variables – 你可以这样做:

 static public void RaiseEvent(this EventHandler @event, object sender, EventArgs e) { if (@event != null) @event(sender, e); } static public void RaiseEvent<T>(this EventHandler<T> @event, object sender, T e) where T : EventArgs { if (@event != null) @event(sender, e); } 

就个人而言,我不会使用关键字作为参数名称,但它根本不会真正改变主叫方,所以做你想要的东西:)

编辑:至于“OnXXX”方法:你计划从你派生的类? 在我看来,大部分class级都应该被封存。 如果你这样做 ,你想让这些派生类能够提高事件吗? 如果对这些问题中的任何一个的答案是“不”,那么不要麻烦。 如果两者的答案都是“是”,那么请:)

现在C#6在这里,有一个更紧凑的,线程安全的方式来触发一个事件:

 SomethingHappened?.Invoke(this, e); 

Invoke()仅在委托注册为事件(即不为空)时才被Invoke() ,这要归功于空条件运算符“?”。

问题中的“处理程序”代码的线程问题在这里被回避,因为在该代码中, SomethingHappened只能被访问一次,所以在testing和调用之间不可能被设置为null。

这个答案或许与原来的问题相切,但对于那些寻找更简单的方法来提高事件的人来说,这是相当有利的。

[这是一个想法]

只需按照推荐的方式编写一次代码即可完成。 那么你不会混淆你的同事们看着代码,认为你做错了什么?

[我读了更多的post,试图find写作事件处理程序的方法,而不是我花费时间编写事件处理程序。]

代码less,可读性强。 我喜欢。

如果你对性能不感兴趣,你可以像这样声明你的事件以避免null检查:

 public event EventHandler SomethingHappened = delegate{}; 

通过将处理程序分配给局部variables,不能“确保”线程安全。 分配完成后,您的方法仍然可能会中断。 例如,如果在中断期间用于监听事件的类被丢弃,则您正在调用处置类中的方法。

正如Jon Skeet和Cristianlibardo在答案中指出的那样,您可以从空值引用例外中拯救自己。

另一件事是对于非密封类,OnFoo方法应该是虚拟的,我认为这是不可能的扩展方法。