我们都熟悉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(this EventHandler @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); }
我的问题:这是个好主意吗?没有标准的On方法,我们是否遗漏了什么?(我注意到的一件事是它不适用于具有显式添加/删除代码的事件.)
它仍然适用于具有显式添加/删除的事件 - 您只需要使用委托变量(或者您已经存储了委托)而不是事件名称.
但是,有一种更简单的方法可以使其成为线程安全的 - 使用no-op处理程序初始化它:
public event EventHandler SomethingHappened = delegate {};
调用额外委托的性能可以忽略不计,它确实使代码更容易.
顺便说一句,在您的扩展方法中,您不需要额外的局部变量 - 您可以这样做:
static public void RaiseEvent(this EventHandler @event, object sender, EventArgs e) { if (@event != null) @event(sender, e); } static public void RaiseEvent(this EventHandler @event, object sender, T e) where T : EventArgs { if (@event != null) @event(sender, e); }
我个人不会使用关键字作为参数名称,但它根本没有真正改变调用方面,所以做你想要的:)
编辑:至于"OnXXX"方法:你是否计划派生你的课程?在我看来,大多数课程都应该密封.如果这样做,您是否希望这些派生类能够引发事件?如果这些问题中的任何一个的答案都是"不",那就不要打扰了.如果两者的答案都是"是",那么:)
现在C#6在这里,有一种更紧凑,线程安全的方式来触发事件:
SomethingHappened?.Invoke(this, e);
Invoke()
只有委托者为事件注册时才会被调用(即它不为空),这要归功于空条件运算符"?".
问题所解决的"处理程序"代码的线程问题在这里被回避,因为,就像在该代码中一样,SomethingHappened
只访问一次,因此在测试和调用之间不可能将其设置为null.
这个答案可能与原始问题相关,但对那些寻找更简单方法来提高事件的人来说非常相关.
[这是一个想法]
只需以推荐的方式编写代码一次并完成它.那么你不会混淆你的同事看着代码,认为你做错了什么?
[我阅读了更多关于编写事件处理程序的方法,而不是编写事件处理程序.]