How do closures differ between foreach and list.Fo

2019-02-10 09:17发布

Consider this code.

        var values = new List<int> {123, 432, 768};

        var funcs = new List<Func<int>>();

        values.ForEach(v=>funcs.Add(()=>v));

        funcs.ForEach(f=>Console.WriteLine(f()));//prints 123,432,768

        funcs.Clear();

        foreach (var v1 in values)
        {
            funcs.Add(()=>v1);
        }

        foreach (var func in funcs)
        {
            Console.WriteLine(func());  //prints 768,768,768
        } 

I know that the second foreach prints 768 3 times because of the closure variable captured by the lambda. why does it not happen in the first case?How does foreach keyword different from the method Foreach? Is it beacuse the expression is evaluated when i do values.ForEach

2条回答
2楼-- · 2019-02-10 09:28

The difference is that in the foreach loop, you've got a single variable v1 which is captured. That variable takes on each value within values - but you're only using it at the end... which means we only see the final value each time.

In your List<T>.ForEach version, each iteration introduces a new variable (the parameter f) - so each lambda expression is capturing a separate variable, which never changes in value.

Eric Lippert has blogged about this - but note that this behaviour may change in future versions of C#.

查看更多
三岁会撩人
3楼-- · 2019-02-10 09:32

foreach only introduces one variable. While the lambda parameter variable is "fresh" each time it is invoked.

Compare with:

foreach (var v1 in values) // v1 *same* variable each loop, value changed
{
   var freshV1 = v1; // freshV1 is *new* variable each loop
   funcs.Add(() => freshV1);
} 

foreach (var func in funcs)
{
   Console.WriteLine(func()); //prints 123,432,768
}

That is,

foreach (T v in ...) { }

can be thought of as:

T v;
foreach(v in ...) {}

Happy coding.

查看更多
登录 后发表回答