Anonymous Delegates and Outer Variables Capturing

time to read 2 min | 353 words

The following code doesn’t do what I want it to do:

 

List<Action<string>> actions = new List<Action<string>>();

int[] arr = { 1, 2, 3, 4, 5 };

foreach (int i in arr)

{

    actions.Add(delegate{ Console.WriteLine(i); });

}

foreach (Action<string> action in actions)

{

    action(null);

}

 

The issue turns out to be local variable lexical scoping. To simplify the spec, if an anonymous delegate needs to capture an outer variable (be it a method parameter, local variable, etc), it will get created in the lexical scope of the variable. If there are several of those, in the deepest lexical scope, of course.

The issue above is what the lexical scope of the “int i" variable is. Considering that you can’t use the variable outside the loop, and that you can define another variable afterward with the same name, I assumed that the lexical scope of the variable is the loop. It looks like I was mistaken. The code above would output five lines of 5.

The variable scope is the method scope. The workaround for this is to add a new variable inside the loop, and assign the variable to it.

This is covered in the C# Spec in section 21.5.2 and it is damn annoying.