给出以下(经过大量编辑的伪代码):
int count = 0;
thing.Stub(m => m.AddBlah()).WhenCalled(o => count++);
thing.Stub(m => m.RemoveBlah()).WhenCalled(o => count--);
DoStuff(thing);
Assert.AreEqual(1, count);
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]);
没有警告。
为什么使用阵列安全?
答案 0 :(得分:13)
我自己在ReSharper中注意到了同样的事情,并且还想知道为什么当值被包装在数组中时它不会发出警告。不幸的是,这里的另一个答案是错误的,并且似乎误解了闭包是如何实现的,所以我想我会尝试解释(我认为)这种重构背后的基本原理。
正如您所见,无论数组是否包装,结果都是相同的,因此重构不会真正“修复”任何内容,并且在应用之后存在访问普通修改后的闭包时可能遇到的相同问题更改。但是,在更改后,count
数组本身未被修改(仅限其内容),“访问已修改的闭包”警告不再相关。
这种变化并没有真正使问题变得更加明显(至少在我看来),所以看起来这个建议实际上是在告诉ReSharper忽略这个问题,而不必诉诸于相当混乱的{{ 1}}抑制错误的机制。
答案 1 :(得分:1)
这是因为两种类型不同。 int是Value类型,数组是Reference类型。这意味着int在堆栈上,数组的指针在堆栈上。
更新Value类型时,它会更新该堆栈内存。另一方面,Reference类型仅留下堆栈内存并修改它指向的内容。
Resharper没有抱怨数组,因为2种不同的Lambda方法在内存周围创建了一个闭包,指向更新值的位置。两个Lambdas都获得相同的地址,并且不会更改原始地址。