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();