代码检查:Lambda 表达式/匿名方法不得捕获其包含上下文
在 C# 中,Lambda 表达式通常会带来一些性能和内存影响。 尽管 Lambda 表达式提供的抽象在大多数情况下值得额外的开销,但对于某些热点代码,这些开销可能是不可接受的。
为了区分此类代码,您可以使用来自 JetBrains.Annotations的 [RequireStaticDelegate] 属性。 此属性通过报告传递给带有此属性注释的参数的 Lambda 表达式中包含上下文的捕获,强制执行无分配的编码实践。 没有任何包含上下文捕获的 Lambda 表达式仅分配一次委托实例,并在程序执行的其余部分缓存它,因此此类 Lambda 表达式变得无分配。
以下是一个 API 示例,它期望用户使用 Func 委托类型输入参数计算缓存值。
class Cache
{
private Dictionary<string, int> _cache = new();
public int GetData(string key,
[RequireStaticDelegate] Func<string, int> calculator)
{
if (!_cache.TryGetValue(key, out var value))
{
value = _cache[key] = calculator(key);
}
return value;
}
}
……但很容易出错,捕获变量 key 而不是 x 参数:
class CacheTest
{
public CacheTest()
{
var cache = new Cache();
var key = "abc";
// Warning: anonymous function
// should not have captures of the containing context
var a = cache.GetData(key, calculator: x => key.Length);
}
}
JetBrains Rider 将报告此类捕获,但不会建议任何 快速修复——您需要确保代码在不捕获任何调用上下文的情况下正常工作。
如果您将方法作为委托参数传递,JetBrains Rider 将建议在可能的情况下为 Lambda 表达式添加 静态 修饰符,以在编译器级别防止捕获。
// Allocates on each call
var a = cache.GetData(key, calculator: Compute);
// Doesn't allocate
var b = cache.GetData(key, calculator: static x => Compute(x));
最后修改日期: 2025年 9月 26日