代码之家  ›  专栏  ›  技术社区  ›  Sarah Vessels

Resharper中修改的关闭警告

  •  4
  • Sarah Vessels  · 技术社区  · 14 年前

    我希望有人能向我解释这段代码中会发生什么坏事,这会导致Resharper给出“访问修改后的闭包”警告:

    bool result = true;
    
    foreach (string key in keys.TakeWhile(key => result))
    {
        result = result && ContainsKey(key);
    }
    
    return result;
    

    即使上面的代码看起来是安全的,在其他“修改后的闭包”实例中会发生什么坏事?我经常看到这个警告是使用LINQ查询的结果,我倾向于忽略它,因为我不知道什么地方会出错。Resharper试图通过使第二个变量对我来说毫无意义来解决问题,例如它改变了 foreach 线以上:

    bool result1 = result;
    foreach (string key in keys.TakeWhile(key => result1))
    

    更新: 另一方面,显然整个代码块可以转换为以下语句,这不会导致修改后的关闭警告:

    return keys.Aggregate(
        true,
        (current, key) => current && ContainsKey(key)
    );
    
    5 回复  |  直到 11 年前
        1
  •  6
  •   SLaks    14 年前

    当你修改 result 变量,闭包(在lambda表达式中使用变量)将获取更改。

    对于不完全理解闭包的程序员来说,这常常是一个意外的惊喜,因此Resharper对此有一个警告。

    通过使 result1 只在lambda表达式中使用的变量,它将忽略对原始变量的任何后续更改。 结果 变量。

    在代码中,依赖于闭包获取对原始变量的更改,以便它知道何时停止。

    顺便说一下,不使用LINQ编写函数的最简单方法如下:

    foreach (string key in keys) {
        if (ContainsKey(key))
            return true;   
    }
    return false;
    

    使用LINQ,您可以简单地调用 Any() :

    return keys.Any<string>(ContainsKey);
    
        2
  •  8
  •   Dave D    14 年前

    在该特定代码中,没有任何内容,请以以下内容为例:

    int myVal = 2;
    
    var results = myDatabase.Table.Where(record => record.Value == myVal);
    
    myVal = 3;
    
    foreach( var myResult in results )
    {
        // TODO: stuff
    }
    

    结果似乎会返回值所在的所有记录 2 因为当您声明查询时,myval就是这样设置的。但是,由于延迟执行,实际上将是值为 3 因为在迭代查询之前不会执行查询。

    在您的示例中,该值没有被修改,但resharper警告您该值可能被修改,并且延迟执行可能会导致您出现问题。

        3
  •  3
  •   Eric Lippert    11 年前

    http://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/

    对于这个问题的广泛讨论,以及我们在假设的未来版本的C中可以做什么来减轻它。

        4
  •  2
  •   Daniel Plaisted    14 年前

    我不确定Resharper是否会对此给出完全相同的警告,但下面说明了类似的情况。循环的迭代器在LINQ子句中使用,但直到循环完成后才实际计算该子句,此时迭代器变量已更改。下面是一个人为设计的示例,它看起来应该打印1到100之间的所有奇数,但实际上打印1到99之间的所有数字。

    var notEven = Enumerable.Range(1,100);
    foreach (int i in Enumerable.Range(1, 50))
    {
        notEven = notEven.Where(s => s != i * 2);
    }
    
    Console.WriteLine(notEven.Count());
    Console.WriteLine(string.Join(", ", notEven.Select(s => s.ToString()).ToArray()));
    

    这可能是一个很容易犯的错误。我听过有人说,如果您真正理解闭包/函数式编程等,就不应该犯这种错误。我也见过专业的开发人员,他们确实很好地掌握了这些概念,犯了这个错误。

        5
  •  0
  •   Femaref    14 年前

    好吧,警告是因为 result 无法在执行关闭之前进行更改(变量在执行时获取,而不是定义)。在你的例子中,你实际上利用了这个事实。如果您使用resharper重新合并,它实际上会破坏您的代码,如 key => result1 总是返回真。