Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why am I getting wrong results when calling Func<int>?

Tags:

c#

lambda

I have the following code snippet in C#:

var actions = new List<Func<int>>();

IEnumerable<int> values = new List<int> { 1, 2, 3 };

foreach (int value in values)
{
    actions.Add(() => value * value);
}

foreach (var action in actions)
{
    Console.WriteLine(action()); ;
}

Console.ReadLine();

It's running fine, but I am not getting the result I expect.

Actual result

9,9,9

Expected result

1,4,9

Why am I not getting the result I expect?

like image 336
santosh singh Avatar asked Mar 24 '11 15:03

santosh singh


2 Answers

You need to capture the variable inside the loop. Right now your delayed execution actions are using the last value of value from the first foreach loop.

var actions = new List<Func<int>>();
        IEnumerable<int> values = new List<int> { 1, 2, 3 };
        foreach (int value in values)
        {
            var v = value;
            actions.Add(() => v * v);
        }
        foreach (var action in actions)
        {
            Console.WriteLine(action()); ;
        }
        Console.ReadLine();

Note the var v = value; line.

like image 122
Enigmativity Avatar answered Nov 11 '22 01:11

Enigmativity


You're capturing the loop variable within your lambda expression, which means when the delegate is finally invoked, it's seeing the final value of the loop variable.

Simple fix:

foreach (int value in values)
{
    int copy = value;
    actions.Add(() => copy * copy);
}

This way you get a new copy variable in each iteration of the loop, so each delegate expression will capture a different variable, and they're not affected by the loop variable (value) changing over time.

Eric Lippert explains this well in "Closing over the loop variable considered harmful" (and part two).

Basically this is a "gotcha" in C# which nearly everyone falls for sooner or later.

like image 43
Jon Skeet Avatar answered Nov 11 '22 02:11

Jon Skeet