bufferz
bufferz

Reputation: 3476

Retrieving lambda expressions from a collection

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

Answers (3)

SLaks
SLaks

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

Dave Mateer
Dave Mateer

Reputation: 17946

I think you're seeing the behaviour described here:

http://blogs.msdn.com/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx

Upvotes: 6

Ray Vernagus
Ray Vernagus

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

Related Questions