假设有以下代码段(我使用的.net 4.0):
private void CauseTrouble()
{
if (MyEvent != null)
{
DoSomeIrrelevantCalculations();
MyEvent();
}
}
在运行包含此(简化)方法的相当大的程序时,在极少数情况下,我会遇到MyEvent()的NullReferenceException。调试器告诉我,即使我只是检查它不是零的,即使我的myevent是无效的。这里发生了什么?
现在有些人可能会立即说:"当然,如果您有一个多线程应用程序,另一个线程可能会在这些无关的计算完成之前就没有注册myEvent,并且 - 繁荣!
这肯定是正确的,但是在我更深入地进入多线程的黑暗之前(由于这是一个非常罕见的错误,我经常必须等待几天才能再次发生),我需要知道是否还有其他可能性为此发生?
特别是:成功的非空检查后,事件是否为null 必然是意味着另一个线程未注册事件?
(我可以想象的另一个选项是该程序也执行的外部非托管代码,并且以前已经造成了一些数据访问。是否有可能在那里的某些操作导致指向MyEvent的指针被覆盖?还是由于代码段是托管代码的一部分,这是不可能的吗?我对这些内容没有经验。)
如果您现在说:嘿,为什么您不记录所有注册/解放流程并检查自己发生的事情:您是对的。我已经在上面了。但是正如我所说,运气不好,可能需要一个星期才能得到答案,并且我也想在此期间检查其他可能性。
update
事情变得陌生。假设是正确的,调试器表明,在被调用时,MyEvent确实是无效的。所以我做了这里推荐的事情并取代了
if (MyEvent != null)
{
DoSomeIrrelevantCalculations();
MyEvent();
}
var handler = MyEvent;
if (handler != null)
{
DoSomeIrrelevantCalculations();
handler();
}
,但这仍然产生nullReferenceException。调试表明,当调用Handler()时,MyEvent再次为NULL,但处理程序本身不是。如果对象不为null,
您有两个选择。如果您在C#6.0或更高版本中工作,则可以使用NULL合并操作员。在幕后,它将防止此问题发生。以下是:
MyEvent?.Invoke();
如果您不使用C#6.0,另一个选项将很有用。只需首先将其存储在变量中,检查变量,然后使用局部变量调用它,例如:
var eventHandler = MyEvent;
if (eventHandler != null)
{
eventHandler();
}