为什么 Resharper 报 "access to modified closure" 错误?

3

如果这是一个重复的问题,我很抱歉。在这里我看到了许多关于“修改闭包”的问题,但似乎没有解决我所遇到的问题。

Resharper 2016.2将我的lambda表达式中使用的“b”和“i1”标记为“访问已修改的闭包”。我认为它不应该这样做。我从未在程序的其他地方使用过b或i或i1。它们都在循环内部声明 - 实际上,i1是由Resharper创建的,以解决i的修改闭包问题。我可以尝试使用i1来解决它,但它只会创建一个“int i2 = i1”,并仍然给出i2的警告!这肯定是不对的。我错过了什么?

for (int i = 0; i < 10; ++i) {
    Button b = new Button();
    int i1 = i;
    Invoker.SyncInvoke(b, () => { b.Text = "number " + i1; });
}

编辑: 这一定是一个bug。在某个特定的源文件中(确切地说,在第424行),有以下三行:

        var collapsed = daSheet;
        int numrows = collapsed.GetLastNonEmptyRow(NonEmptyItemFlag.Data);
        int numcols = collapsed.GetLastNonEmptyColumn(NonEmptyItemFlag.Data);

如果我将上面的代码片段(即上面的for循环)粘贴到那些行之前,就不会收到警告。如果我将它粘贴在那些行之后,就会收到关于"b"和"i1"的警告。如果我将它粘贴在第二行和第三行之间,我会收到关于"i1"的警告,但不是关于"b"的警告。这毫无意义。

1
对于在Resharper上工作的许多聪明人,我并不是有意冒犯,但他们分析器的误报率相当可怕。我认为你没有漏掉任何东西,而是这不是一个很好的分析器。 - Eric Lippert
使用 Invoker.SyncInvoke 是多余的;一个按钮被创建并且文本被赋值,但是这个按钮还没有被添加到控件集合中。 - Metro Smurf
如果这段代码是一个简化的例子,那么它可能被简化得太多了,但很难确定。 - jdphenix
这是导致问题的确切代码片段吗?因为如果我将其粘贴到新项目中(并创建Invoker类),则根本不会收到警告。 - citizenmatt
是的,这就是确切的片段,而且是过于简化了。只有最少量的代码才能让问题出现。奇怪的是,如果我将其粘贴到新项目或任何较小的项目中,我不会收到警告,但如果我将其粘贴到我的大型项目中,我就会收到警告。每次都是如此。没有其他变量或任何地方叫做“b”或“i1”。 - Timothy Blaisdell
帖子中添加了更多信息... - Timothy Blaisdell
1个回答

0

“他们分析仪的误报率相当糟糕” - 你说得对^^

事实上,Resharper并没有那么错。

想象一下,如果不使用Invoker,而是使用类似于Task.Startnew(...)这样的东西,我会假设(只读源码)执行后你不会有10个不同的按钮,你只有一个按钮,上面写着“数字9”。

因为编译器将按钮和i1参数放在lambda表达式附近,所以通常值得查看用dotpeek反编译后的源代码 ?


你错了。你一定会有10个按钮,因为你调用了“new Button()”10次。如果你在lambda中使用循环计数器“i”,那么所有的按钮都可能(或取决于代码实际执行的时间)显示为“button 9”。创建一个本地范围的变量(被lambda捕获)可以使它正常工作。Resharper在这件事上是错误的。 - Timothy Blaisdell

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