Recently I’ve been researching about the behavior of foreach loop for C# in 5.0 and earlier versions. Here’s a snippet that 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 how this comes to happen. so I studied the implementation of 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 was Anonymous methods uses captured local variables. If a variable is declared locally then if your anonymous method is using an outer variable from 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 outer variable.
Now let’s take a deep dive in 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 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 the scenarios what is happening in case of outer variable captured values and vice versa.
Outer variable captured values:
Inner local variable captured values:
This is the update that happened in the new Implementation of C#. Now the well known bug is gone that people faced earlier version of C# when working with anonymous methods.
Hope you enjoyed it.
Reference: Annotated Csharp standards
No comments:
Post a Comment