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.
note
As explained in the Eric Lippert's post, the behavior of foreach was changed in C# 5.0 (Visual Studio 2012) so that a new loop variable is generated for each iteration making it safe to use in closures.
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 =newList<Action>();var myStrings =newList<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 to fix it by copying the value of the loop variable to the scope where the closure is defined:
var myActions =newList<Action>();var myStrings =newList<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.
Our website uses some cookies and records your IP address for the purposes of accessibility, security, and managing your access to the telecommunication network. You can disable data collection and cookies by changing your browser settings, but it may affect how this website functions. Learn more.
With your consent, JetBrains may also use cookies and your IP address to collect individual statistics and provide you with personalized offers and ads subject to the Privacy Notice and the Terms of Use. JetBrains may use third-party services for this purpose. You can adjust or withdraw your consent at any time by visiting the Opt-Out page.