正如在这个问题中看到的那样: 使用扩展方法引发 C# 事件 - 是不是很糟糕?
我正在考虑使用这种扩展方法来安全地引发事件:
public static void SafeRaise(this EventHandler handler, object sender, EventArgs e)
{
if (handler != null)
handler(sender, e);
}
但是 Mike Rosenblum 在 Jon Skeet 的回答中提出了这种担忧:
你们需要将 [MethodImpl(MethodImplOptions.NoInlining)] 属性添加到这些扩展方法中,否则 JITter 可能会优化您将委托复制到临时变量的尝试,从而允许出现空引用异常。
我在 Release 模式下进行了一些测试,以查看当扩展方法未使用 NoInlining 标记时是否可以获得竞争条件:
int n;
EventHandler myListener = (sender, e) => { n = 1; };
EventHandler myEvent = null;
Thread t1 = new Thread(() =>
{
while (true)
{
//This could cause a NullReferenceException
//In fact it will only cause an exception in:
// debug x86, debug x64 and release x86
//why doesn't it throw in release x64?
//if (myEvent != null)
// myEvent(null, EventArgs.Empty);
myEvent.SafeRaise(null, EventArgs.Empty);
}
});
Thread t2 = new Thread(() =>
{
while (true)
{
myEvent += myListener;
myEvent -= myListener;
}
});
t1.Start();
t2.Start();
我在发布模式下运行了一段时间的测试,但从未出现过 NullReferenceException。
那么,Mike Rosenblum 在他的评论和方法内联不能导致竞争条件中是错误的吗?
事实上,我想真正的问题是,SaifeRaise 是否会被内联为:
while (true)
{
EventHandler handler = myEvent;
if (handler != null)
handler(null, EventArgs.Empty);
}
或者
while (true)
{
if (myEvent != null)
myEvent(null, EventArgs.Empty);
}