【问题标题】:Access to modified closure - ref int访问修改后的闭包 - ref int
【发布时间】:2012-09-14 19:37:56
【问题描述】:
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 ?

【问题讨论】:

  • 实际场景中有异步服务调用,我想知道是否所有的item都被验证过,以便让用户知道。
  • 也许是因为 Resharper 很烂? (严肃的问题,因为你的代码运行良好)

标签: c#-4.0 lambda resharper resharper-6.0


【解决方案1】:

这种情况经常发生

ReSharper 警告您 count 被您分配为“验证完成”事件处理程序的 lambda 隐式捕获,并且它的值很可能在 lambda 创建时间之间发生变化(即当您分配事件时) handler) 以及它被调用的时间。如果发生这种情况,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 - 这可能被称为“显而易见的”,但对于许多不熟悉 lambda 机制的开发人员来说,这是令人惊讶和违反直觉的.

而我们通常是这样解决的

“关闭 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# 识别的特殊行 cmets 禁用此警告。

【讨论】:

    猜你喜欢
    • 2011-09-29
    • 2011-07-28
    • 2010-09-23
    • 2015-11-08
    • 2010-12-13
    • 2016-05-31
    • 2011-12-18
    • 1970-01-01
    相关资源
    最近更新 更多