为什么resharper会建议;将变量包装在数组中";访问修改的关闭警告?

为什么resharper会建议;将变量包装在数组中";访问修改的关闭警告?,resharper,closures,Resharper,Closures,给出以下(大量编辑的伪)代码: ReSharper在计数时提供警告-“访问修改的闭包”。我理解为什么我会收到这个警告(count变量在两个不同的lambda中被修改,并且可能有不需要的语义),但我不理解ReSharper的建议:“在数组中包装局部变量”。如果我让ReSharper这么做,我会得到: int count[] = { 0 }; thing.Stub(m => m.AddBlah()).WhenCalled(o => count[0]++); thing.Stub(m =&

给出以下(大量编辑的伪)代码:

ReSharper在计数时提供警告-“访问修改的闭包”。我理解为什么我会收到这个警告(count变量在两个不同的lambda中被修改,并且可能有不需要的语义),但我不理解ReSharper的建议:“在数组中包装局部变量”。如果我让ReSharper这么做,我会得到:

int count[] = { 0 };
thing.Stub(m => m.AddBlah()).WhenCalled(o => count[0]++);
thing.Stub(m => m.RemoveBlah()).WhenCalled(o => count[0]--);

DoStuff(thing);

Assert.AreEqual(1, count[0]);
没有警告


为什么使用阵列是安全的?

这是因为这两种类型不同。int是值类型,数组是引用类型。这意味着int在堆栈上,数组的指针在堆栈上

当您更新一个值类型时,它会更新该堆栈内存。另一方面,引用类型单独保留堆栈内存,并修改它指向的内容


Resharper没有抱怨数组,因为两个不同的Lambda方法正在内存周围创建一个闭包,指向更新值的位置。这两个lambda都获得相同的地址,并且不更改原始地址。

我自己在ReSharper中注意到了同样的事情,并且还在纳闷为什么当值包装在数组中时它不发出警告。不幸的是,这里的另一个答案是错误的,似乎误解了闭包是如何实现的,所以我想尝试解释(我认为是什么)这种重构背后的基本原理

正如您所看到的,无论是否对数组进行包装,结果都是相同的,因此重构并没有真正“修复”任何东西,并且在应用更改后,访问普通修改的闭包时可能会遇到相同的问题。但是,由于未修改
计数
数组本身(仅其内容),因此更改后,“访问已修改的闭包”警告不再相关


这一变化并没有让问题变得更加明显(至少在我看来),因此这一建议实际上是在告诉ReSharper忽略这个问题,不必求助于相当混乱的
//ReSharper disable AccessToModifiedClosure
机制来抑制错误。

但是,围绕int变量闭合的lambda肯定也都指向内存中的同一点,那么有什么区别呢?在这两种情况下,代码执行相同,因此在实现方面没有区别。但是我不明白为什么resharper会对它们有不同的对待。@citizenmatt,想想当方法返回时的情况,resharper不知道lambdas只会在您调用DoStuff(thing)时运行。这不是正确的原因,看看另一个答案。在lambda中捕获引用或值变量没有区别,这就是为什么行为是相同的。您在哪个版本的ReSharper中看到了这一点?这似乎是他们的基本原理,因为我想不出这个代码“修复”的任何其他原因。如果这确实是他们实现此功能的原因,那么奇怪的是,他们会认为将对变量的所有访问包装在一个元素数组中比仅在使用时使用注释禁用警告要简单得多。
int count[] = { 0 };
thing.Stub(m => m.AddBlah()).WhenCalled(o => count[0]++);
thing.Stub(m => m.RemoveBlah()).WhenCalled(o => count[0]--);

DoStuff(thing);

Assert.AreEqual(1, count[0]);