【问题标题】:Multiple closures and ReSharper's "Access to Modified Closure" Warning多个闭包和 ReSharper 的“访问修改的闭包”警告
【发布时间】:2011-07-29 19:05:05
【问题描述】:

我有如下代码:

private void SetupCheeseShop(Button buyCheese, Button spoilCheese)
{
    var cheeseCount = 0;    // No cheese

    spoilCheese.Click += (sender, e) => {
        // "Access to Modified Closure" warning occurs for cheeseCount below:
        MessageBox.Show(string.Format("{0} cheeses have spoiled", cheeseCount));

        cheeseCount = 0;    // Throw out moldy cheese
    };


    buyCheese.Click += (sender, e) => {
        cheeseCount++;
    };
}

ReSharper 警告我,当我在糟蹋奶酪处理程序中读取 cheeseCount 时,我正在访问一个修改后的闭包。在这种情况下我可以放心地忽略它吗?

我希望在对第一个闭包的调用之间修改奶酪计数,但我不确定当进行修改的代码在同一个变量周围的第二个闭包中时会发生什么。

【问题讨论】:

    标签: c# resharper closures


    【解决方案1】:

    在您的情况下,您可以放心地忽略警告。它警告您,在创建闭包时,已关闭的变量可能具有与当前值 (0) 不同的值。这就是你想要的。你可以有任意数量的闭包,它们都引用同一个变量。将闭包变量视为类中的一个字段(这是编译器生成的)。

    C# Language Specification 4.0 的第 5.1.7 节所述:

    如果局部变量被匿名函数捕获 (§7.15.5.1),它的生命周期至少延长到委托或 从匿名函数创建的表达式树,以及任何 引用捕获变量的其他对象是 符合垃圾收集条件。

    您可以放心地依赖此行为:变量保持不变,无论有多少匿名函数引用它,直到收集事件处理程序,在您的示例中收集按钮本身之前不会发生这种情况。

    【讨论】:

    • 我知道编译器会为一些闭包生成类,但不是每个闭包都有自己的类(因此是变量)吗?还是只为两个闭包创建一个类?
    • @Cameron 我相信它目前会生成一个类,尽管这是一个实现细节而不是规范中的。事实上,在未来的版本中很有可能会发生变化,因为当前的实现可能存在资源保留问题。
    • @dlev:好的。但是如果每个闭包都有自己的类,他们会访问不同的变量,对吧?那么我的例子就完全行不通了。
    • @Cameron 对。该示例有效,因为只有一个生成的闭包类(您可以使用 Reflector/ILSpy 轻松验证)。
    • 规范保证变量总是相同的。查看我的帖子的更新。
    【解决方案2】:

    您的 cheeseCount 变量不是一个字段,您的变量范围仅在您自己的方法内。

    您应该将您的 cheeseCount 从您的方法中移出并使其成为一个字段。

    【讨论】:

    • 碰巧这个方法是一个奶酪处理工厂(好吧,我稍微扩展一下我的例子),所以每次调用我的方法时我都想要一个新的cheeseCount 变量。
    • 是的,每次调用方法时都会有一个新变量,以及带有新 cheeseCount 变量的新匿名事件处理程序。其他领域更有意义。在您的情况下,它可能有效,但在某些情况下,它可能真的会产生不可预测的结果,因此会发出警告。
    猜你喜欢
    • 2010-12-13
    • 2015-11-08
    • 2012-09-14
    • 2011-02-26
    • 2011-02-05
    • 2013-09-22
    • 1970-01-01
    • 2011-09-29
    相关资源
    最近更新 更多