C# Closure is not working as expected

I can't understand quite clearly the difference between two blocks of code. Consider there is a program

    class Program
{
    static void Main(string[] args)
    {

        List<Number> numbers = new List<Number>
                                   {
                                       new Number(1),
                                       new Number(2),
                                       new Number(3)
                                   };


        List<Action> actions = new List<Action>();
        foreach (Number numb in numbers)
        {
            actions.Add(() => WriteNumber(numb));
        }

        Number number = null;
        IEnumerator<Number> enumerator = numbers.GetEnumerator();
        while (enumerator.MoveNext())
        {
            number = enumerator.Current;
            actions.Add(() => WriteNumber(number));
        }

        foreach (Action action in actions)
        {
            action();
        }

        Console.ReadKey();


    }

    public static void WriteNumber(Number num)
    {
        Console.WriteLine(num.Value);
    }

    public class Number
    {
        public int Value;

        public Number(int i)
        {
            this.Value = i;
        }

    }

}

The output is

1
2
3
3
3
3    

These two blocks of code should work identically. But you can see that the closure is not working for the first loop. What am i missing?

Thanks in advance.

Jon Skeet
people
quotationmark

These two blocks of code should work identically.

No they shouldn't - at least in C# 5. In C# 3 and 4 they would, in fact.

But in the foreach loop, in C# 5, you have one variable per iteration of the loop. Your lambda expression captures that variable. Subsequent iterations of the loop create different variables which don't affect the previously-captured variable.

In the while loop, you have one variable which all the iterations capture. Changes to that variable will be seen in all of the delegates that captured it. You can see this by adding this line after your while loop:

number = new Number(999);

Then your output would be

1
2 
3
999
999
999

Now in C# 3 and 4, the foreach specification was basically broken by design - it would capture a single variable across all iterations. This was then fixed in C# 5 to use a separate variable per iteration, which is basically what you always want with that sort of code.

people

See more on this question at Stackoverflow