1

Possible Duplicate:
Is there a reason for C#'s reuse of the variable in a foreach?
Looping through a list of Actions

today I encounter a problem about the C# foreach function, it didn't give me the proper result as I expected. here is the code:

using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
class Program
{
    static void Main(string[] args)
    {
        int[] data = new int[] { 1, 2, 3, 4, 5 };
        List<Func<int>> actions = new List<Func<int>>();
        foreach (int x in data)
        {
            actions.Add(delegate() { return x; });
        }
        foreach (var foo in actions)
        {
            Console.WriteLine(foo());   
        }
        Console.ReadKey();
    }
}
}

when I Run it in console application and it has five 5 printed on the screen. Why? I just cann't understand. Have asked some people and they just said that there is closure in this code, But I am not very clear about this, I remember that in javascript , I often encounter the closure, but in above code, why there is closure? thx.

Community
  • 1
  • 1
CharlieShi
  • 888
  • 3
  • 17
  • 43
  • 3
    Have a look: http://stackoverflow.com/questions/9569334/looping-through-a-list-of-actions/ – Matthias Meid Mar 26 '12 at 14:23
  • 2
    http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx – Daniel Schlößer Mar 26 '12 at 14:24
  • This question is asked almost every day. See http://stackoverflow.com/questions/8898925/is-there-a-reason-for-cs-reuse-of-the-variable-in-a-foreach/8899347#8899347 for a good discussion of the issue. – Eric Lippert Mar 26 '12 at 14:47

2 Answers2

6

In C#4 all iterations of a foreach loop share the same variable, and thus the same closure.

The specification says:

foreach (V v in x) embedded-statement 

is then expanded to:

{  
  E e = ((C)(x)).GetEnumerator(); 
  try
  { 
    V v; 
    while (e.MoveNext())
    { 
      v = (V)(T)e.Current; 

      embedded-statement 
    } 
  } 
  finally
  {  
    … // Dispose e 
  } 
}

You can see that v is declared in a block outside the while-loop, which causes this sharing behavior.

This will probably be changed in C#5.

We are taking the breaking change. In C# 5, the loop variable of a foreach will be logically inside the loop, and therefore closures will close over a fresh copy of the variable each time. The "for" loop will not be changed.

http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
  • why this earlier spec says v is declared inside the while loop http://msdn.microsoft.com/en-GB/library/aa664754.aspx – colinfang Apr 29 '13 at 16:03
2

The key is that when you are creating the delegates within your foreach loop you are creating a closure over the loop variable x, not its current value.

Only when you execute the delegates in actions will the value be determined, which is the value of x at that time. Since you have completed the foreach loop by then the value will be the last item in your data array which is 5.

BrokenGlass
  • 158,293
  • 28
  • 286
  • 335