Skip to content

Foreach variable capture fix C# 5.0correctness

Each iteration of `foreach` now captures its own copy of the loop variable.

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, Charlie
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: 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 LangVersion on a modern compiler does not bring back the old behavior
  • The for loop was not changed: for (int i = ...) still reuses the same variable, so closure capture in for loops 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.

More information ​

Powered by Intracia