ReSharper 2016.3 Help

Code Inspection: Access to foreach variable in closure

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 it's 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

ReSharper detects this problem and suggests you 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(str); }); } 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.

See Also

Last modified: 15 February 2017