4

The demo code has some problems.

var values = new List<int>() { 100, 110, 120 };  
var funcs = new List<Func<int>>();  

foreach(var v in values)   
    funcs.Add( ()=>v );  

foreach(var f in funcs)   
    Console.WriteLine(f());  

Most people expect it to be 100 / 110 / 120. It is in fact 120 / 120 / 120.

but the result in vs2015 & .net 4.5.1 will output 100 / 110 / 120, not 120 / 120 / 120.

And when I test the code as follows, there are some differences between for and foreach

var values = new List<int> {100, 110, 120};

var funcs = new List<Func<int>>();
foreach (var v in values)
    funcs.Add(() =>
    {
        Console.WriteLine(v);
        return v;
    } );

foreach (var f in funcs)
    Console.WriteLine(f());

//will throw exception
for (int i=0;i<values.Count;i++)
    funcs.Add(() =>
    {
        Console.WriteLine(values[i]);
        return values[i];
    });

foreach (var f in funcs)
    Console.WriteLine(f());

Who can give me some more detail between for and foreach in closures?

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
huoxudong125
  • 1,966
  • 2
  • 26
  • 42
  • You may want to read part 2 including the update http://ericlippert.com/2009/11/16/closing-over-the-loop-variable-considered-harmful-part-two/ . "We are taking the breaking change. In C# 5, the loop variable of a foreach will be logically inside the loop, and therefore closures will close over a fresh copy of the variable each time. The for loop will not be changed. We return you now to our original article." – Chris Oct 22 '15 at 09:59
  • did you saw the update note at the top. – Nikita Shrivastava Oct 22 '15 at 10:04

1 Answers1

6

This is the result of an unfortunate decision that was later regretted by the C# team. A breaking change introduced by C# 5 finally changed that behavior. Quoting Eric Lippert:

In C# 5 the foreach loop variable will be logically inside the body of the loop, and therefore closures will get a fresh copy every time.

Before C# 5, the closures all referenced the same variable binding. Therefore, the output was the same for all invocations because they were all accessing the latest value of the same variable.

Starting with C# 5, however, a new variable binding is created for each iteration. This is the behavior that was most likely intended by the programmer.

Community
  • 1
  • 1
Marius Schulz
  • 15,976
  • 12
  • 63
  • 97
  • 2
    Its also worth noting that in a for loop it clearly must use the same variable for each iteration of the loop. – Chris Oct 22 '15 at 10:02
  • @Chris Well, it's not that it *must*, it's just that it seems clear that it ought to, from the syntax. They *could* create a copy of the variable on each iteration if they wanted to. – Servy Oct 23 '15 at 14:06