Dot Net Gotcha #2 – Loop variables and Closures
This one is my favorite.
Can you guess the output of this simple console application:
class Program { static void Main() { var actions = new List(); for (var i = 0 ; i < 10; i++) { var writeToConsoleAction = new Action(() => { Console.WriteLine(i); }); actions.Add(writeToConsoleAction); } foreach (var action in actions) { action(); } Console.ReadLine(); } }
One would expect to see numbers from 0 to 9 but here is the actual output of the app:
It turns out its like that by design. What you have there is a Closure over the loop variable. And closures in C# are done around variables and not around specific values of those variables – so that means that lambda expression gets to use the actual reference to the closed variable.
Let me explain in little bit more detail:
In first loop we iterate increasing the counter variable from 0 to 9 and then use it in the lambda expression. That means each lambda gets its access to the counter via closure.
So at the end of this first loop, value of the counter is 10 (its 10 because we used post-increment operator: counter++, so in last iteration of the loop we increase counter to 10 and then check if its <10 and we see its not, so we exit the loop).
Afterwards we start executing those actions we created in first loop, and they all have a Closure over this same variable counter – which has value of 10 and therefore each action prints out its current value 10 to the console.
So it works as expected, once you know what to expect. 🙂
And this is really common mistake C# developers make.
How to fix it?
Solution is very simple once you know what is going on. All we need to do is this: instead of passing the variable counter to the lambda expression, create a local copy of this variable and pass this copy to the lambda instead of passing the counter.
That way, closure will each time be done around that copy variable that has current value of counter at that moment of execution, and this local copy value stay that way and will not be changed afterwards by the loop.
Later when the actions are executed (in the second loop), each will use their own closure around copy of the counter and therefore each will have its own different, expected value.
Here is the code that works as we expect:
class Program { static void Main() { var actions = new List(); for (var i = 0 ; i < 10; i++) { int counterCopy = i; var writeToConsoleAction = new Action(() => { Console.WriteLine(counterCopy); }); actions.Add(writeToConsoleAction); } foreach (var action in actions) { action(); } Console.ReadLine(); } }
As you see we create a copy of counter called counterCopy and pass this to the lambda expression each time.
Although this seems like old news if you knew it, there are many C# developers that are not aware of this behavior (or tend to forget it from time to time) so make sure to spread the word and always remember it.
By the way, the C# team is changing this in C# version 5 to work as one would expect but until then we just need to copy those loop variables manually 🙂
Excellent stuff, very informative. That IS a gotcha indeed!
Thanks Per,
I’m glad you found this interesting.
I figured the more we write/read about these kind of .NET details, there should be less bugs in our code 🙂