Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

odd lambda behavior

I stumbled across this article and found it very interesting, so I ran some tests on my own:

Test One:

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

for (int i = 0; i < 5; ++i)
    actions.Add(() => Console.WriteLine(i));

foreach (Action action in actions)
    action();

Outputs:

5
5
5
5
5

Test Two:

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

for (int i = 0; i < 5; ++i)
{
    int j = i;
    actions.Add(() => Console.WriteLine(j));
}

foreach (Action action in actions)
    action();

Outputs:

0
1
2
3
4

According to the article, in Test One all of the lambdas contain a reference to i which causes them to all output 5. Does that mean I get the expected results in Test Two because a new int is created for each lambda expression?

like image 925
tbridge Avatar asked Nov 14 '11 02:11

tbridge


3 Answers

This is because of variable capturing in C# that can be a little tricky

In a nutshell, Each loop of the for loop is referring to the same variable i so the compiler uses the same lambda expression for all loops.

If it is any consolation, This oddity is worse in javascript as javascript only has function scopes for variables so even your second solution won't do what you expect it to.

This is also a very good explanation

like image 112
parapura rajkumar Avatar answered Nov 14 '22 16:11

parapura rajkumar


@Eric Lippert has explained this in great detail in his two-parts article:

  • Closing over the loop variable considered harmful
  • Closing over the loop variable, part two

It is a must-read article, as it explains the behavior in depth and at implementation-level.

like image 7
Nawaz Avatar answered Nov 14 '22 17:11

Nawaz


Yes.

In Test One the var i is captured in the loop, but i refers to a variable that is effectively declared once outside the loop, so all of the captured lambdas refer to the one variable. By the time you call the actions the value of i is 5 so all of the output is five.

In Test Two the var j is captured in the loop, but in this case j is declared each time inside the loop, so all of the captured lambdas refer to the distinct variables. So calling the lambdas outputs the distinct values.

like image 1
Enigmativity Avatar answered Nov 14 '22 15:11

Enigmativity