Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c#

lambda

Suppose we have the following code:

void AFunction()
{

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

void Main()
{
    AFunction();
    foreach( var i in listOfLambdaFunctions)
       i();
}

One might think that the above code would out the same as the following:

void Main()
{

    foreach(AClass i in AClassCollection)
       PrintLine(i.name);
}

However, it doesn't. Instead, it prints the name of the last item in AClassCollection every time.

It appears as if the same item was being used in each lambda function. I suspect there might be some delay from when the lambda was created to when the lambda took a snapshot of the external variables used in it. Essentially, the lambda is holding a reference to the local variable i, instead of taking a "snapshot" of i's value when the lambda was created.

To test this theory, I tried this code:

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

and, surprise, it outputs changed!

I am using XNA 3.1, and whichever version of C# that comes with it.


My questions are:

  1. What is going on?
  2. Does the lambda function somehow store a 'reference' to the variable or something?
  3. Is there any way around this problem?
like image 607
matt Avatar asked Nov 11 '10 14:11

matt


1 Answers

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); }  ); 
   } 
like image 134
Chris Baxter Avatar answered Sep 21 '22 13:09

Chris Baxter