Reputation: 3476
I'm very new to lambda expressions in C#, and I'm having trouble conceptualizing how they are stored/retrieved in a collection.
I'm trying to programatically create a list of 10 Funcs x => x + 1, x => x + 2, etc. as a test. My desired output is 0,1,2,3,4,5,6,7,8,9
Here is my code for that:
var list = new List<Func<int, int>>();
for (int i = 0; i < 10; i++)
{
Func<int, int> func = x => x + i;
Console.WriteLine("a) " + func.Invoke(0)); //returns 0,1,2,3,4,5,6,7,8,9
list.Add(func);
Console.WriteLine("b) " + list[i].Invoke(0)); //returns 0,1,2,3,4,5,6,7,8,9
}
foreach (var func in list) //returns 10,10,10,10,10,10,10,10,10,10
Console.WriteLine("c) " + func.Invoke(0));
for(int i = 0; i < list.Count; i++) //returns 10,10,10,10,10,10,10,10,10,10
Console.WriteLine("d) " + list[i].Invoke(0));
I get the same results when substituting a Func array for the List[Func].
What am I missing?
Upvotes: 4
Views: 441
Reputation: 887449
Your problem is that the lambda expression captures the variable you used to define it.
Since the entire loop the generated the list shares the same variable, all of the delegates will return 10.
To solve this problem, declare a separate variable inside the generating loop, assign it to i
, then use it in the lambda expression.
For example:
var list = new List<Func<int, int>>();
for (int dontUse = 0; dontUse < 10; dontUse++) {
var i = dontUse;
Func<int, int> func = x => x + i;
list.Add(func);
}
foreach (var func in list)
Console.WriteLine("c) " + func(0));
For more information, see this blog post
By the way, when calling the delegates, you don't need to write func.Invoke(params)
; you can simply write func(params)
.
Upvotes: 4
Reputation: 6150
Make i local to the lambda by copying it into a new variable:
var list = new List<Func<int, int>>();
for (int i = 0; i < 10; i++)
{
var temp = i;
Func<int, int> func = x => x + temp;
Console.WriteLine("a) " + func.Invoke(0)); //returns 0,1,2,3,4,5,6,7,8,9
list.Add(func);
Console.WriteLine("b) " + list[i].Invoke(0)); //returns 0,1,2,3,4,5,6,7,8,9
}
foreach (var func in list) //returns 0,1,2,3,4,5,6,7,8,9
Console.WriteLine("c) " + func.Invoke(0));
for (int i = 0; i < list.Count; i++) //returns 0,1,2,3,4,5,6,7,8,9
Console.WriteLine("d) " + list[i].Invoke(0));
Upvotes: 7