C#中的闭包分配问题

27

我已经安装了 Clr Heap Allocation Analyzer 扩展程序,在一个项目中我看到了一些我不太理解的东西,我有一个带有签名的方法

public Task<int> ExecuteAsync(string sql, dynamic param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
{
    param = SetModificationValuesForGlobalRing(param);
    return _sqlPolicy.ExecuteAsync(async () =>
    {
        int result;
        using (var connection = new SqlConnection(_connectionString))
        {
            await connection.OpenAsync();
            result = await connection.ExecuteAsync(sql, param as object, transaction, commandTimeout, commandType);
        }
        return result;
    });
}

这个工具在方法和所有参数上给了我一个警告,说:

编译器将会生成一个类来将此作为字段持有,以允许捕获此闭包。

我不知道这种行为发生的原因,是因为可选参数吗?

1个回答

36

您必须是以下情况之一:

  1. 在匿名函数中调用此代码,例如 lambda 函数。

    • 或 -
  2. 在某个地方使用 yield/await 关键字。

当您使用以上任意一种方法时,C#必须创建一个闭包来捕获在闭包外部定义范围内使用的任何变量(在第一种情况下)或在 yield/await 之前和之后使用的任何变量(在第二种情况下)。

C#通过在内存中创建一个在编译时定义的匿名类来捕获闭包。然后,它为该匿名类创建一个字段,用于存储需要持久化的每个信息。这可能会导致对象实例的生命周期延长到闭包或使用 yield/await 的方法的整个生命周期。

有时,它的生命周期比您未使用 yield/await 或 lambda 时更长。在这种情况下,您可能会注意到内存使用量比您预期的要高(因为垃圾收集器只有在闭包完全超出作用域或包含 yield/await 的方法已完成时才会收集对象实例)。

您看到的警告只是工具试图向您解释上述内容,以便让您知道要预期此行为和由此可能导致的内存使用量增加。


12
在匿名函数(例如lambda)中调用这段代码。我认为相反是正确的:方法体包含一个lambda函数。 - svick
我同意@svick的观点,这个方法使用了lambda表达式,这是导致这种行为的原因。我有很多带有async的方法,但它们并不会导致工具显示这个信息。 - evilpilaf
你说得对,我在方法中看到了他的lambda表达式,而且看起来“param”是被闭包捕获的。 - Ayo I
@AyoI,您介意修改答案以反映这一点吗? - Sedat Kapanoglu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接