Home > Back-end >  Unexpected result when looping over List<Action>
Unexpected result when looping over List<Action>

Time:04-10

I'm currently looking for tricky interview snippets and I found two that I cannot explain. I merged them together so they can be run at the same time.

Here is the code:

using System.Collections.Generic;
public class Program
{
    public static void Main(string[] args)
    {
        var intActions = new List<Action>();
        for (int i = 0; i < 4; i  )
            intActions.Add(() => { Console.WriteLine(i); });

        foreach (var action in intActions)
            action();


        string[] strings = { "abc", "def", "ghi" };
        var stringActions = new List<Action>();
        foreach (string str in strings)
            stringActions.Add(() => { Console.WriteLine(str); });

        foreach (var action in stringActions)
            action();
    }
}

The output is:

4
4
4
4
abc
def
ghi

Can anyone explain me why is the result like this? I'd expect four "4"s with four "ghi"s or "0123" and "abc def ghi"

CodePudding user response:

The reason why you are seeing 4444 and not 0123, & ghi ghi ghi instead of abc def ghi is due to closures.

The i variable passed to the delegate is passed by reference and not by value which means that all of the actions will point to the same memory location for variable i & the latest value for i (set to 4 on the last iteration of the loop).

For the output to be 0123, copying the variable to another temp variable would mean each action would have a pointer to a separate memory location, and thus yield the numbers as 'expected'.

var intActions = new List<Action>();
for (int i = 0; i < 4; i  ) {
    int copy = i;
    intActions.Add(() => { Console.WriteLine(copy); });
}

foreach (var action in intActions)
    action();

The same concept applies to the second part of your example:

string[] strings = { "abc", "def", "ghi" };
var stringActions = new List<Action>();
foreach (string str in strings) {
    var copy = str;
    stringActions.Add(() => { Console.WriteLine(copy); });
}

foreach (var action in stringActions)
    action();
  • Related