我见过的大多数代码都使用以下方式来声明和调用事件触发:
public class MyExample
{
public event Action MyEvent; // could be an event EventHandler<EventArgs>, too
private void OnMyEvent()
{
var handler = this.MyEvent; // copy before access (to aviod race cond.)
if (handler != null)
{
handler();
}
}
public void DoSomeThingsAndFireEvent()
{
// ... doing some things here
OnMyEvent();
}
}
甚至 ReSharper 也会按照上述方式生成调用方法。
为什么不这样做:
public class MyExample
{
public event Action MyEvent = delegate {}; // init here, so it's never null
public void DoSomeThingsAndFireEvent()
{
// ... doing some things here
OnMyEvent(); // save to call directly because this can't be null
}
}
谁能解释为什么不这样做的原因? (优点与缺点)
优点和缺点是:
空检查非常便宜;在说话十亿分之一一秒钟的时间。分配一个委托,然后在该对象的剩余生命周期内对其进行垃圾回收,可能需要花费一些时间百万分之一一秒钟的时间。另外,你正在消费几十个字节更多内存。另外,每次事件触发时,您都会不必要地调用一个不执行任何操作的方法,从而消耗更多微秒。如果您是那种关心百万分之一秒和几十个字节的人,那么这可能是一个有意义的差异;绝大多数情况下不会。
您必须记住始终创建空委托。这真的比记住检查 null 更容易吗?
这两种模式实际上都使事件成为线程安全的。这两种模式仍然完全有可能在一个线程上触发事件处理程序,同时在另一个线程上删除事件处理程序,这意味着它们会发生竞争。如果您的处理程序删除代码破坏了处理程序所需的状态,则一个线程可能正在破坏该状态,而另一个线程正在运行该处理程序。不要认为仅仅检查 null 或分配一个空处理程序就可以神奇地消除竞争条件。它只是消除了导致取消引用 null 的竞争条件。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)