In C# 4.0 and earlier the foreach loop reused the same variable for each iteration. This meant that closures (such as lambda expressions or delegates) created inside the loop all captured the same variable and would see its final value rather than the value at the time they were created.
This was a very common source of bugs, especially when creating tasks, event handlers, or LINQ queries inside a loop.
C# 5.0 changed the behavior so that each iteration of foreach gets its own copy of the loop variable, making closure capture work as most developers expect.
Code
csharp
var actions = new List<Action>();
foreach (var name in new[] { "Alice", "Bob", "Charlie" })
actions.Add(() => Console.WriteLine(name));
foreach (var action in actions)
action();
// Output: Alice, Bob, Charliecsharp
var actions = new List<Action>();
foreach (var name in new[] { "Alice", "Bob", "Charlie" })
actions.Add(() => Console.WriteLine(name));
foreach (var action in actions)
action();
// Output: Charlie, Charlie, Charlie (all captured the same variable)Notes
- This was a breaking change but was considered acceptable because the old behavior was almost never intentional
- The change is in the compiler rather than tied to the language version, so setting an older
LangVersionon a modern compiler does not bring back the old behavior - The
forloop was not changed:for (int i = ...)still reuses the same variable, so closure capture inforloops still requires a local copy - Eric Lippert described the original behavior as "the single most common incorrect bug report" for the C# compiler
- See Local function statements C# 7.0 for local functions, which obey the same capture rules as a lambda.