C# lambda, local variable value not taken when you think?

后端 未结 5 778
清歌不尽
清歌不尽 2020-12-06 17:37

Suppose we have the following code:

void AFunction()
{

   foreach(AClass i in AClassCollection)
   {
      listOfLamb         


        
相关标签:
5条回答
  • 2020-12-06 18:00

    This is a modified closure

    See: similar questions like Access to Modified Closure

    To work around the issue you have to store a copy of the variable inside the scope of the for loop:

       foreach(AClass i in AClassCollection) 
       { 
          AClass anotherI= i;
          listOfLambdaFunctions.AddLast(  () =>  {  PrintLine(anotherI.name); }  ); 
       } 
    
    0 讨论(0)
  • 2020-12-06 18:03

    I've been caught by this one as well, as said by Calgary Coder, it is a modified closure. I really had trouble spotting them until I got resharper. Since it is one of the warnings that resharper watches for, I am much better at identifying them as I code.

    0 讨论(0)
  • 2020-12-06 18:11

    Yes, the lambda stores a reference to the variable (conceptually speaking, anyway).

    A very simple workaround is this:

     foreach(AClass i in AClassCollection)
       {
          AClass j = i;
          listOfLambdaFunctions.AddLast(  () =>  {  PrintLine(j.name); }  );
       }
    

    In every iteration of the foreach loop, a new j gets created, which the lambda captures. i on the other hand, is the same variable throughout, but gets updated with every iteration (so all the lambdas end up seeing the last value)

    And I agree that this is a bit surprising. :)

    0 讨论(0)
  • 2020-12-06 18:12

    does the lambda function somehow store a 'reference' to the variable or something?

    Close. The lambda function captures the variable itself. There is no need to store a reference to a variable, and in fact, in .NET it is impossible to permanently store a reference to a variable. You just capture the entire variable. You never capture the value of the variable.

    Remember, a variable is a storage location. The name "i" refers to a particular storage location, and in your case, it always refers to the same storage location.

    Is there anyway around this problem?

    Yes. Create a new variable every time through the loop. The closure then captures a different variable every time.

    This is one of the most frequently reported problems with C#. We're considering changing the semantics of the loop variable declaration so that a new variable is created every time through the loop.

    For more details on this issue see my articles on the subject:

    http://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/

    0 讨论(0)
  • 2020-12-06 18:20

    what is going on? does the lambda function somehow store a 'reference' to the variable or something?

    Yes exactly that; c# captured variables are to the variable, not the value of the variable. You can usually get around this by introducing a temp variable and binding to that:

    string astr = "a string";
    var tmp = astr;
    AFunc fnc = () => { System.Diagnostics.Debug.WriteLine(tmp); };
    

    especially in foreach where this is notorious.

    0 讨论(0)
提交回复
热议问题