假设有以下代码片段(我使用的是.NET 4.0):
private void CauseTrouble()
{
if (MyEvent != null)
{
DoSomeIrrelevantCalculations();
MyEvent();
}
}
当运行包含此(简化)方法的相当大的程序时,在极少数情况下,我会遇到来自MyEvent()的NullReferenceException。调试器告诉我MyEvent是null,即使我刚检查它不是null。这是怎么回事?
现在有些人可能会立即说:"当然,如果你有一个多线程应用程序,另一个线程可能会在完成那些不相关的计算之前取消注册myEvent - Boom! - 你有一个NullReferenceException!"
这肯定是正确的,但在我深入探究多线程的黑暗之前(因为这是一个非常罕见的错误,我经常需要等待几天再次发生),我需要知道是否还有其他可能性这会发生什么?
具体来说:成功的非空检查后,事件是否为空必然意味着另一个线程未注册该事件?
(我可以想象的另一个选项是外部非托管代码,它也是由程序执行的,并且之前已经导致了一些数据访问冲突。是否有可能某些操作导致指向MyEvent的指针被覆盖?或者这应该是不可能的,因为代码片段是托管代码的一部分?我对这些东西的经验太缺乏了。)
如果你现在说:嘿,你为什么不记录所有注册/取消注册过程并检查自己发生了什么:你是对的。我已经在上面了。但正如我所说,运气稍差,我可能需要一周的时间才能得到答案,而且我想在此期间查看其他可能性。
更新
事情变得陌生。假设是正确的,调试器显示MyEvent在被调用时确实为null。所以我做了这里推荐的并取代了
if (MyEvent != null)
{
DoSomeIrrelevantCalculations();
MyEvent();
}
与
var handler = MyEvent;
if (handler != null)
{
DoSomeIrrelevantCalculations();
handler();
}
但是这仍然会产生NullReferenceException。调试显示调用handler()时MyEvent再次为null,但处理程序本身不是。如果对象甚至不为null,如何引发NullReferenceException?
答案 0 :(得分:3)
您有两种选择。如果您使用的是C#6.0或更高版本,则可以使用null合并运算符。在幕后,它可以防止这个问题的发生。方法如下:
MyEvent?.Invoke();
如果您不使用C#6.0,则另一个选项很有用。首先将它存储在变量中,检查变量,然后使用局部变量调用它,如下所示:
var eventHandler = MyEvent;
if (eventHandler != null)
{
eventHandler();
}