2010-10-22 32 views
5

在这里看到了几个类似的问题,但没有一个似乎完全是我的问题...访问修改后的封闭......但为什么?

我理解(或认为我理解)闭包的概念,并理解什么会导致Resharper抱怨访问修改后的闭包,但在下面的代码中,我不明白我是如何违反闭包的。

因为primaryApps是在for循环的上下文中声明的,所以在处理primaryAppsprimary不会改变。如果我已经在for循环之外声明了primaryApps,那么绝对,我有闭包问题。但为什么在下面的代码?

var primaries = (from row in openRequestsDataSet.AppPrimaries 
       select row.User).Distinct(); 

    foreach (string primary in primaries) { 

     // Complains because 'primary' is accessing a modified closure 
     var primaryApps = openRequestsDataSet.AppPrimaries.Select(x => x.User == primary); 

是ReSharper的只是没有足够的智慧弄清楚它是不是一个问题,或者是有封闭一个原因是一个问题,在这里,我没有看到?

+1

您确定要使用'选择'而不是'Where'吗? – diceguyd30 2010-10-22 17:39:32

+0

Select返回一个有类型数据行的数组...其中返回它们的IEnumerable。任何一个人都会为我正在做的事情工作;我读过的文章说。在我使用的条件下,选择更快。也许是这样,也许不是很难说。 – 2010-10-22 19:10:38

+0

我拿回来...因为我有一个lambda,我没有使用内置的datatable.select(),所以我没有得到一个类型的行数组。在这种情况下,你是对的,我没有改变价值观,所以一个地方更有意义。 – 2010-10-22 19:27:59

回答

10

的问题是在下面的语句

由于primaryApps的for循环的范围内声明的,主要是不会,而我处理primaryApps改变。

Resharper根本没有办法100%验证这一点。在这里引用闭包的lambda被传递到这个循环的上下文之外:AppPrimaries.Select方法。该函数本身可以存储生成的委托表达式,稍后执行并直接进入迭代变量问题的捕获。

正确地检测这是否可能是一项艰巨的任务,坦率地说不值得。相反,ReSharper正在采取安全路线并警告有关潜在危险的迭代变量捕获。

+0

要添加到此,Visual Basic的编译器会精确地给出警告同样的情况。 C#团队已经考虑采用VB/Resharper警告,但推迟到下一个版本为止。 – 2010-10-22 18:37:15

+1

@Jonathan无耻的插件,这里是一篇深入解释VB.Net消息的文章http://blogs.msdn.com/b/jaredpar/archive/2007/07/26/closures-in-vb-part-5-looping .aspx – JaredPar 2010-10-22 18:41:40

+1

因此,根据你和Eric的说法,问题是'AppPrimaries.Select()'是静态的,可以将lambda存储在一个静态变量中,另一次调用'AppPrimaries.CalculateSelectLambda()'会导致延迟执行,直到原始循环很长时间完成。在这一点上,'primary'会保存通过循环上次设置的值。 – 2010-10-22 19:54:02

1

就我所知,Resharper每次访问foreach变量时都会生成警告,即使它并未真正导致关闭。

+0

只有当您在lambda或其他延迟执行上下文中使用该变量时 – 2010-10-22 20:01:40

0

是的,它只是警告, 看: http://devnet.jetbrains.net/thread/273042

+0

是的,我知道这只是一个警告......只是需要知道我是否可以放心地忽略它:)哪个答案显然是'不' – 2010-10-22 20:01:10

8

由于primaryApps的for循环的范围内声明的,主要是不会,而我处理primaryApps改变。如果我已经在for循环之外声明了primaryApp,那么绝对会有闭包问题。但为什么在下面的代码?

Jared是对的;为了说明为什么你的结论不符合逻辑地从你的前提出发,让我们制作一个程序,在for循环的上下文中声明primaryApps,仍然遭受捕获的循环变量问题。很容易做到这一点。

static class Extensions 
{ 
    public IEnumerable<int> Select(this IEnumerable<int> items, Func<int, bool> selector) 
    { 
     C.list.Add(selector); 
     return System.Enumerable.Select(items, selector); 
    } 
} 

class C 
{ 
    public static List<Func<int, bool>> list = new List<Func<int, bool>>(); 
    public static void M() 
    { 
     int[] primaries = { 10, 20, 30}; 
     int[] secondaries = { 11, 21, 30}; 

     foreach (int primary in primaries) 
     { 
      var primaryApps = secondaries.Select(x => x == primary); 
      // do something with primaryApps 
     } 
     C.N(); 
    } 
    public static void N() 
    { 
     Console.WriteLine(C.list[0](10)); // true or false? 
    } 
} 

其中 “primaryApps” 被宣布为完全不相干。唯一相关的是闭包可能在环路中存活,因此有人可能会在稍后调用它,错误地期望在闭包中捕获的变量被值捕获。

Resharper无法知道Select的特定实现不会为稍后存储选择器;实际上,这正是他们所有人做的。 Resharper应该如何知道他们恰好将它藏在一个以后无法访问的地方?

+0

我认为你的意思是“Resharper”,而不是“Reflector”;) – 2010-10-22 18:10:41

+0

我会强调警告与'primary'在循环外声明有关,而不是在循环内声明'primaryApps'。 – Gabe 2010-10-22 18:35:00