我常常看到这样的代码在源代码中分散和重复:
var handler = MyEvent;
if (handler != null)
{
handler.Invoke(null, e);
}
有没有理由不将它封装在像这样的扩展方法中?
public static void SafeInvoke<T>(this EventHandler<T> theEvent, object sender, T e) where T : EventArgs
{
var handler = theEvent;
if (handler != null)
{
handler.Invoke(sender, e);
}
}
这样就可以这样调用:
MyEvent.SafeInvoke(this, new MyEventArgs(myData));
答案 0 :(得分:3)
这是一个有趣的问题。杰弗里里希特通过C#在 CLR中对此进行了很多讨论。
使用如下所示的空检查: -
var handler = MyEvent;
if (handler != null)
{
handler.Invoke(null, e);
}
JIT编译器可能完全优化掉handler
变量。但是,CLR团队意识到许多开发人员使用此模式来引发事件并将此感知构建到JIT编译器中。这可能会在CLR的所有未来版本中保留,因为更改行为可能会破坏太多现有应用程序。
没有理由你不能写一个扩展方法来为你做这项工作(这正是我所做的)。请注意,方法本身可能是JIT内联(但临时变量仍然不会被优化掉)。
如果您设想将您的应用程序用于其他运行时,例如Mono(我怀疑它反映了CLR在这种情况下的行为)或者可能出现的其他一些奇特的运行时,然后你可以通过在扩展方法中添加[MethodImplAttribute(MethodImplOptions.NoInlining)]
来防止你的代码反对JIT内联的可能性。
如果您决定不使用扩展方法,另一种防止JIT优化的方法(Jeffrey Richter建议)是以JIT编译器无法优化的方式将值分配给临时变量,例如< / p>
var handler = Interlocked.CompareExchange(ref MyEvent, null, null);
答案 1 :(得分:0)
答案 2 :(得分:0)
如果模式困扰你,你可以自由地创建这样的扩展方法。
我能看到的唯一缺点是扩展方法类所在的命名空间的依赖性增加,以及其他开发人员需要浪费3到5秒的时间来解决SafeInvoke的问题。