int count = itemsToValidate.Count;
foreach(var item in itemsToValidate)
{
item.ValidateAsync += (x, y) => this.HandleValidate(ref count);
}
private void HandleValidate(ref int x)
{
--x;
if (x == 0)
{
// All items are validated.
}
}
对于上面的代码,resharper抱怨“访问Modified Closure”。如果我将其更改为对象类型,则不会这样做。为什么这是一个闭包,即使我经过ref?
答案 0 :(得分:4)
ReSharper警告您,count
被您分配为“验证完成”事件处理程序的lambda隐式捕获,并且它的值可能在创建lambda的时间(即分配时)之间发生变化事件处理程序)以及调用它的时间。如果发生这种情况,lambda将看不到人们直觉所期望的值。
一个例子:
int count = itemsToValidate.Count;
foreach(var item in itemsToValidate)
{
item.ValidateAsync += (x, y) => this.HandleValidate(ref count);
}
// afterwards, at some point before the handlers get invoked:
count = 0;
在这种情况下,处理程序会将count的值读作0
而不是itemsToValidate.Count
- 这可能被称为“显而易见”,但对于许多不熟悉的开发人员来说,这是令人惊讶和反直觉的lambdas的机制。
“关闭R#up”的常用解决方案是将捕获的变量移动到内部作用域中,在该作用域中它很容易访问,并且R#可以证明在评估lambda之前不能修改它:
int count = itemsToValidate.Count;
foreach(var item in itemsToValidate)
{
int inner = count; // this makes inner impossible to modify
item.ValidateAsync += (x, y) => this.HandleValidate(ref inner);
}
// now this will of course not affect what the lambdas do
count = 0;
您的特定情况是一个相对罕见的情况,您明确地想要这种行为,并且使用上述技巧实际上会使程序行为不正确(您需要捕获的引用指向相同的计数)
正确的解决方案:使用R#识别的特殊行注释禁用此警告。