Solving Issues With LINQ Foreach Variables
Can you guess what the output of the following program is?
using System;
using System.Collections.Generic;
namespace Lambda
{
class Program
{
static void Main(string[] args)
{
var strings = new[] {"a", "b", "c"};
var actions = CreateActions(strings);
actions.ForEach(f => f());
}
private static List CreateActions(IEnumerable strings)
{
var actions = new List();
foreach (var s in strings)
actions.Add( () => Console.WriteLine(s) );
return actions;
}
}
}
I expected to see all three strings (a, b and c), but instead I got this:

While analyzing the IL code with Reflector I learned a thing or two about lambdas in C#.
When a lambda is encountered by the compiler, it generates a class which has a field for each local variable used by the lambda. In this case, the generated class would look like this in C#:
private sealed class <>c_DisplayClass3
{
public string s;
public void b_0()
{ Console.WriteLine(this.s); }
}
Of course, the above is not valid C# due to invalid characters in identifiers, but is pretty much what is produced in the IL.
So far so good – no real surprises here. However, look at this code which is my translation from IL to C# of the CreateActions method:
private static ListCreateActions(IEnumerable strings) { List actions = new List (); using (IEnumerator enumerator = strings.GetEnumerator()) { // Note 1: The lambda is created outside of the loop <>c_DisplayClass3 lambda = new <>c_DisplayClass3(); string s; while (enumerator.MoveNext()) { s = enumerator.Current; // Note 2: The instance field is reassigned each time lambda.s = s; actions.Add(lambda); } } return actions; }
The reason for the strange output is clear now – the compiler did not create a lambda object in each iteration but rather reused the existing one each time. After the loop, all items in the actions list are actually the same instance and their s field is the last element of the collection. Apparently, C# compiler creates a lambda instance immediately before the local variable it uses. So, in order to force it to create a new lambda for each iteration, we need to introduce a new local variable in the loop:
private static ListCreateActions(IEnumerable strings) { var actions = new List (); foreach (var s in strings) { // This variable will be used in the lambda var lambda_param = s; actions.Add( () => Console.WriteLine(lambda_param) ); } return actions; }
With this modification the program now produces this output:

loading...
loading...
No related posts.
Related posts brought to you by Yet Another Related Posts Plugin.
