问题描述
昨晚在审查代码时,我发现了这段代码。值得注意的是,state
是通过执行泛型类型回调操作的 lambda 捕获的。
public static void Post<TState>(TState state,Action<TState> callback){
if(SynchronizationContext.Current is SynchronizationContext currentContext)
// Use capture-semantics for state
currentContext.Post(_ => callback(state),null);
else{
callback(state);
}
}
Post
但是,原生支持 state
的 pass-thru,所以上面也可以这样重写...
public static void Post<TState>(TState state,Action<TState> callback){
if(SynchronizationContext.Current is SynchronizationContext currentContext)
// Use pass-thru state,not 'capture'
currentContext.Post(passedBackState => callback((TState)passedBackState),state);
else{
callback(state);
}
}
我的问题很简单......在这个特定用例中,回调定义在与需要传递给它的状态相同的范围内,是否有任何技术 使用捕获语义(第一个)而不是传递(第二个)的好处和/或缺点?我个人喜欢第一个,因为只有一个变量,但我问的是技术差异,如果两者似乎都有效。
解决方法
闭包在堆上分配一个需要被垃圾回收的新对象。通过 state 参数传递数据不会分配(除非数据是需要装箱的值类型)。
这些分配可能会增加常用方法或长寿命进程(如 Web 应用程序),从而导致 CPU 周期浪费用于垃圾处理和延迟。积极删除此类分配是 .NET Core 比 .NET Old 快得多的原因之一。
像 Roslyn Heap Allocation Analyzer 这样的 Roslyn 分析器突出显示了这样的隐式分配,例如装箱值类型、闭包、使用 params
数组等。
更新
Rider 的 Dynamic Program Analysis 还突出显示了由于闭包、装箱、枚举器等引起的分配。