JetBrains Rider 2024.1 Help

Code Inspection: Access to foreach variable in closure


Potential Code Quality Issues





Default severity




Requires SWA


First of all, let's make sure that you understand what a closure is. To put it simply, a closure in C# is a lambda expression or an anonymous method that captures some variables from an outer scope. Here is the simplest example:

// A self-contained lambda. Not a closure. Action printOne = () => { Console.WriteLine("one"); }; // A closure – a lambda that captures a variable from an outer scope. string myStr = "one"; Action print = () => { Console.WriteLine(myStr); };

In the example above, print will capture the variable myStr (and not its value) and will only get the value of myStr when you invoke print().

In more complex scenarios, when a closure is defined in a changing context, it may not behave as expected.

One of the situations where it may happen is a closure defined inside a foreach statement compiled with C# 4.0 (Visual Studio 2010) or earlier versions.

Before C# 5.0 (Visual Studio 2012), the compiler would expand a foreach to a while statement with the iteration variable defined outside the loop. Therefore, if this variable was used in a closure, later invocations of this closure would always get the value corresponding to the last iteration of the loop:

var myActions = new List<Action>(); var myStrings = new List<string>() { "one", "two", "three" }; foreach (var str in myStrings) { myActions.Add(() => { Console.WriteLine(str); }); } myActions[0](); // "three" is printed

JetBrains Rider detects this problem and suggests to fix it by copying the value of the loop variable to the scope where the closure is defined:

var myActions = new List<Action>(); var myStrings = new List<string>() { "one", "two", "three" }; foreach (var str in myStrings) { var str1 = str; myActions.Add(() => { Console.WriteLine(str1); }); } myActions[0](); // "one" is printed

This fix makes sure that when you pick an action from myActions and get the context where this action was created, str will hold the value of the corresponding iteration.

Last modified: 10 April 2024