Lambda and Scope in C#
Ok, so I’ve read a few articles out there that are trying to explain scope and lambdas. The one above is the latest in an attempt to make it easy to grok this concept in the new C# spec. (new is a relative term) Let me make this very clear and simple.
public static void Main(string[] args) { List<Action> closures = new List<Action>(); for (int i = 0; i < 10; i++) { closures.Add(() => Console.WriteLine(i)); } }
Results:

now, run this Console app and you’re going to get a list of 10s. Here’s where people’s faces turn white and start cursing lambdas. The problem though is that it’s not lambda’s fault at all. In fact, lambda is doing exactly what it is suppose to do. I’ve read online where people debate if lambda’s are lexical at all, because they don’t seem, in this case, to be retaining the correct scope of the variable passed in. Well, this is not true. The issue is the way that for loops execute in C#. You see, according to the C# 3.0 spec the variable “i” in our for loop defines it scope outside of the containing block that it is iterating over. That is the issue. The lambda is going to capture it is in the parent scope and while, traditionally that variable “i” would have fallen out of scope after the for block it is actually retained in memory (because it’s still being referenced by the lambda!) until the lambda is gone.
SO! what do we REALLY want here? Well, if the answer is to print numbers from 0 to 9 then you have to establish a variable to hold the value in the currently executing scope of the lambda. Like so…
public static void Main(string[] args) { List<Action> closures = new List<Action>(); for (int i = 0; i < 10; i++) { int j = i; closures.Add(() => Console.WriteLine(j)); } foreach (var a in closures) a.Invoke(); }
Results:

And there you have it. You’ve now correctly established a variable in the same scope as the lambda thus ensuring it is not modified after you exit that scope. If that's difficult to grok, think back to our garbage collector. It will flag an item for deletion when it falls out of scope and no objects are currently pointing to it in memory. j will be retained in memory because although its execution scope has exited, the lambda is retaining a reference to... even beyond its scope! How cool is that?!
Because some of you may be wondering… why 10’s? Well, again: Scope. the incrementor in our for loop increments AFTER the for block is complete. It is, in essence, a closure itself. If I defined a for loop in C# it might look like this:
static void LeonFor(int start, int end, Action<int> toDo) { int i = start; while (i<end) { toDo.Invoke(i); i++; } }
You see how the scope is defined? The final increment on i will never be seen during the invocation of the block. Thus: 10.
I hope that helps clarify a bit. This is a common barrier to grokking this topic. Remember, it’s not lambda’s that are “acting goofy”. It’s the assumptions we made before they existed. This issue is about Scope and not a “funky new language feature”. And scope is certainly easy to understand, right? :)
articleStats
Here are some silly little facts about this Lambda and Scope in C#...
It was written by Leon 2 months ago.
It has 5528 letters in it.
It has 737 words in it.
It has a total of 3 comments in all.
So far Jacques has the last word!
article Links
These are the links that appear in this article. They probably don't make sense out of context... but just in case. :)
trying to explain
I like the quick fix in Resharper that alerts me about "Access to modified closure" and offers to "Copy to local variable" .. I can spot these problems immediately.