Let first take a prediction, what is the result of the following code.
public static void ClosureForeachWithLambda() { var action = new List<Action>(); var list = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; foreach (var i in list) { action.Add(() => Console.WriteLine(i)); } foreach (var i in action) { i(); } }
It is not 1..9 printed each once, instead it is the 9 printed 9 times. Not belieing, huh, trying it yourself.
So, let me post a complete code with comments to tell why this is happening. Also, in my code below, you will see a fix to the bug in .net 4. 0
public static void Main(string[] args) { ClosureForeachWithLambdaGotha(); } public static void ClosureForeachWithLambda() { // the reason why this is a potential pitfall is because // closure is not creating a copy, not copying the value, but instead it is // the variable itself. // but inside theo foreach loop, // it is loop variable that is bound to and the bound is updated in each loop // so at last, you will see that it is '9' that is printed 9 times var action = new List<Action>(); var list = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; foreach (var i in list) { action.Add(() => Console.WriteLine(i)); } foreach (var i in action) { i(); } } public static void ClosureForeachWithLambdaGotha() { // a fix is to explicitly create a local variable, // by which it is the variable in new scope that is closured // here shows how: // // fortunately thi is going to be fixed in .net 4.5 // however, the bad news is that since 4.5 is going to be a inplace change. // so that means it is going to be a breaking change. var action = new List<Action>(); var list = new [] { 1,2 ,3, 4, 5, 6, 7, 8, 9}; foreach (var i in list) { var copy = i; action.Add(() => Console.WriteLine(copy)); } foreach (var i in action) { i(); } }
As explained in the post : Lifting Foreach, Breaking change in Visual Studio 2012. It is going to be a breaking change, as the .net 4.5 is going to be a in place patch .