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