How do closures differ between foreach and list.ForEach()?
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 metho开发者_开发百科d Foreach
? Is it beacuse the expression is evaluated when i do values.ForEach
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.
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#.
精彩评论