对于扩展方法来说,这是个好主意吗?
本文关键字:好主意 于扩展 扩展 方法 | 更新日期: 2023-09-27 18:10:04
我经常在源代码周围看到这样的代码:
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));
这是一个有趣的问题。Jeffrey Richter通过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编译器无法优化的方式将值赋给临时变量,例如
var handler = Interlocked.CompareExchange(ref MyEvent, null, null);
可能是出于性能考虑。
如果您不喜欢这种模式,您可以自由地创建这样的扩展方法。
我能看到的唯一缺点是增加了对扩展方法类所在的名称空间的依赖,并且其他开发人员将需要浪费3到5秒的时间来计算SafeInvoke的功能。