为什么这个C#程序输出这样的结果?我怎么理解关闭?

时间:2011-03-25 09:00:46

标签: c# .net closures

我试图理解这个问题的答案Why am I getting wrong results when calling Func<int>?我写了一些示例代码。以下代码

public static void Main(string[] args)
{
    var funcs = new List<Func<string>>();
    for(int v=0,i=0;v<3;v++,i++)
    {
        funcs.Add( new Func<string>(delegate(){return "Hello "+ i++ +" "+v;}) );
    }
    foreach(var f in funcs)
        Console.WriteLine(f());
}

生成

Hello 3 3
Hello 4 3
Hello 5 3

在阅读Jon Skeet和Eric Lippert的解释后,我想我会得到

Hello 3 3
Hello 3 3
Hello 3 3

这里v和i都是循环变量,而i的值在那个瞬间被拾取v不是为什么这个?我不明白这种行为。

4 个答案:

答案 0 :(得分:11)

嗯,你正确理解了Eric和Jon,但是你错过了代码的一部分:

"Hello "+ i++ +" "+v;
          ^^^
          this part increments i for each call

基本上,发生的事情与此相似:

  1. 捕获匿名函数3次,捕获方法中对变量的引用,而不是在循环范围内
  2. 在循环结束时,这两个变量的值均为3
  3. 执行第一个函数,输出iv的内容,然后递增i
  4. 执行第二个函数,输出iv的内容,因为这与前一个方法调用相同i,您将在此输出4,而不是3
  5. 另一方面,如果您通过捕获循环范围内的变量来更改代码,请执行以下操作:

    for(int v=0,i=0;v<3;v++,i++)
    {
        int ii = i, vv = v;
        funcs.Add( new Func<string>(delegate(){return "Hello "+ ii++ +" "+vv;}) );
    }
    

    然后你会得到0, 01, 12, 2。你仍然在增加ii变量,你在循环中使用捕获的值之后就这样做了,但是你再也不用那个变量了(每个匿名方法都有它自己的私有副本。)谢谢@ferosekhanj评论

答案 1 :(得分:2)

答案很简单:++i在您的委托中执行,因此每次都会增加值。第一个值为3,因为这是循环后i的值 了解您的委托未在for循环内执行,而是在foreach循环中执行。

答案 2 :(得分:2)

结果是正确的(怎么可能不是?;)) 当你执行委托时,在循环结束后,它将使用i和v变量的当前值。

v将不再更改,循环结束时v == 3。我也= 3。但是你的委托将i写入输出,然后递增它(i ++)。因此,每次执行委托时,我都会增加,但不是v。

这是你所观察到的。

答案 3 :(得分:0)

我认为循环变量具有for循环外部的范围。

public static void Main(string[] args)
{
    var funcs = new List<Func<string>>();
    int i=0;
    for(int v=0;v<3;v++,i++)
    {
        funcs.Add( new Func<string>(delegate(){return "Hello "+ i++ +" "+v;}) );
    }
    foreach(var f in funcs)
        Console.WriteLine(f());
}

for循环i==3v==3之后。由于代码不会在创建委托之间留下i的范围,因此委托的所有三个实例共享相同的i。因此,对函数的每次调用都会增加i345