C# foreach lambda breaks with closure

Let first take a prediction,  what is the result of the following code. 

 

 

 

public static void ClosureForeachWithLambda()
    {
      var action = new List<Action>();
      var list = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
      foreach (var i in list)
      {
        action.Add(() => Console.WriteLine(i));
      }

      foreach (var i in action)
      {
        i();
      }

    }

 

 

It is not 1..9 printed each once, instead it is the 9 printed 9 times. Not belieing, huh, trying it yourself.

 

 

So, let me post a complete code with comments to tell why this is happening.  Also, in my code below, you will see a fix to the bug in .net 4. 0 

 

 

 

public static void Main(string[] args)
    {

      ClosureForeachWithLambdaGotha();
    }



    public static void ClosureForeachWithLambda()
    {
      // the reason why this is a potential pitfall is because 
      // closure is not creating a copy, not copying the value, but instead it is 
      // the variable itself.
      // but inside theo foreach loop, 
      // it is loop variable that is bound to and the bound is updated in each loop
      // so at last, you will see that it is '9' that is printed 9 times
      var action = new List<Action>();
      var list = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
      foreach (var i in list)
      {
        action.Add(() => Console.WriteLine(i));
      }

      foreach (var i in action)
      {
        i();
      }

    }

    public static void ClosureForeachWithLambdaGotha()
    {
      // a fix is to explicitly create a local variable, 
      // by which it is the variable in new scope that is closured
      // here shows how:
      //
      // fortunately thi is going to be fixed in .net 4.5
      // however, the bad news is that since 4.5 is going to be a inplace change. 
      // so that means it is going to be a breaking change.
      var action = new List<Action>();
      var list = new [] { 1,2 ,3, 4, 5, 6, 7, 8, 9};
      foreach (var i in list)
      {
        var copy = i;
        action.Add(() => Console.WriteLine(copy));
      }

      foreach (var i in action)
	    {
        i();
	    }
    }
 

 

As explained in the post  : Lifting Foreach, Breaking change in Visual Studio 2012. It is going to be a breaking change, as the .net 4.5 is going to be a in place patch .

 

你可能感兴趣的:(C# foreach lambda breaks with closure)