2
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 抱怨“访问修改后的闭包”。如果我将其更改为对象类型,则不会这样做。为什么这是一个闭包,即使我经过 ref ?

4

1 回答 1

4

这事儿常常发生

ReSharper 警告您,count您分配为“验证完成”事件处理程序的 lambdas 隐式捕获了它,并且它的值很可能在创建 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#”的通常解决方案是将捕获的变量移动到内部范围内,在该范围内它的可访问性要小得多,并且 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# 识别的特殊行注释禁用此警告。

于 2012-09-22T19:01:43.883 回答