I've been learning C#, and I'm trying to understand lambdas. In this sample below, it prints out 10 ten times.
class Program
{
delegate void Action();
static void Main(string[] args)
{
List<Action> actions = new List<Action>();
for (int i = 0; i < 10; ++i )
actions.Add(()=>Console.WriteLine(i));
foreach (Action a in actions)
a();
}
}
Obviously, the generated class behind the lambda is storing a reference or pointer to the int i
variable, and is assigning a new value to the same reference every time the loop iterates. Is there a way to force the lamda to grab a copy instead, like the C++0x syntax
[&](){ ... } // Capture by reference
vs.
[=](){ ... } // Capture copies
What the compiler is doing is pulling your lambda and any variables captured by the lambda into a compiler generated nested class.
After compilation your example looks a lot like this:
class Program { delegate void Action(); static void Main(string[] args) { List<Action> actions = new List<Action>(); DisplayClass1 displayClass1 = new DisplayClass1(); for (displayClass1.i = 0; displayClass1.i < 10; ++displayClass1.i ) actions.Add(new Action(displayClass1.Lambda)); foreach (Action a in actions) a(); } class DisplayClass1 { int i; void Lambda() { Console.WriteLine(i); } } }
By making a copy within the for loop, the compiler generates new objects in each iteration, like so:
for (int i = 0; i < 10; ++i) { DisplayClass1 displayClass1 = new DisplayClass1(); displayClass1.i = i; actions.Add(new Action(displayClass1.Lambda)); }
The only solution I've been able to find is to make a local copy first:
for (int i = 0; i < 10; ++i) { int copy = i; actions.Add(() => Console.WriteLine(copy)); }
But I'm having trouble understanding why putting a copy inside the for-loop is any different than having the lambda capture i
.
The only solution is to make a local copy and reference that within the lambda. All variables in C# (and VB.Net) when accessed in a closure will have reference semantics vs. copy/value semantics. There is no way to change this behavior in either language.
Note: It doesn't actually compile as a reference. The compiler hoists the variable into a closure class and redirects accesses of "i" into a field "i" inside the given closure class. It's often easier to think of it as reference semantics though.
Remember that lambda expressions are really only syntactic sugar for anonymous methods.
That being said, what you are really looking for is how anonymous methods use local variables in a parent scope.
Here's a link describing this. http://www.codeproject.com/KB/cs/InsideAnonymousMethods.aspx#4
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With