Recently I've been researching the behavior of the foreach loop for C# in 5.0 and earlier versions. Here's a snippet I was trying and getting different output in different versions of C#.
var values = new List<string>() { "Bob", "is", "stupid" };
var funcs = new List<Func<string>>();
foreach (var v in values)
funcs.Add(() => v);
foreach (var f in funcs)
Console.WriteLine(f());
Console.Read();
When I ran this code with Visual Studio 2010 I got the output as:
stupid stupid stupid
But when I tried the same code in Visual Studio 2012 the output was:
Bob is stupid
Which is the correct output as expected. I was wondering why this is. So I studied the implementation of the Foreach loop and found that the earlier versions of C# was designed to optimize the execution of Anonymous methods inside the foreach loop. The point is that Anonymous methods uses captured local variables. If a variable is declared locally then if your anonymous method is using an outer variable from a foreach implementation then the last updated value will be captured. Which is why I was getting the output of the program as "stupid stupid stupid" as this was the last value captured from the outer variable.
Now let's take a deep dive into captured values of variables in anonymous methods.
Outer variable: Any local variable, value parameter, or parameter array whose scope includes an anonymous method expression is called an outer variable of that anonymous-method-expression. In an instance of a function member of a class, the "this" value is considered a value parameter and is an outer variable of any anonymous-method-expression contained within that function member.
Captured value: When an outer variable is referenced by an anonymous method, the outer variable is said to have been captured by the anonymous method. Ordinarily, the lifetime of a local variable is limited to execution of the block or statement with which it is associated. However, the lifetime of a captured outer variable is extended at least until the delegate referring to the anonymous method becomes eligible for garbage collection.
Pitfall of Captured value
A pitfall when creating anonymous methods within a loop is to assume that the control variable of a loop is captured with the value it has at creation. However, as it is the variable (v), and not its value, that is captured, the value at the time the anonymous method is called will be seen. This is often the value at loop termination.
So now you can visualize both scenarios, what is happening in the case of the outer variable's captured values and vice versa.
The Outer variable captured values are as in:
The Inner local variable captured values are as in:
This is an update in the new implementation of C#. Now the well-known bug is gone that people encountered in earlier versions of C# when working with anonymous methods.
Hope you enjoyed it.