2

could you explain me one example with delegate and lambda expression

List<Func<int>> list = new List<Func<int>>(); 
 for (int i = 0; i < 10; i++)
 {
     list.Add(() => i);
 }
 foreach (var func in list)
 {
      Console.WriteLine(func());
 }

I understand, that I have List of refers to methods whithout params and returns Int, but why it returns 10 times max value from loop? How it works? Thx!

Andrey Saw
  • 171
  • 2
  • 7
  • 2
    Because the lambda expression will capture the variable it self, not a copy of its value. After the first loop is done, this variable will have the max value. – Yacoub Massad May 11 '16 at 13:19
  • Because when you run your `Func`s the value of `i` is 10. You need to create a local variable in the `for` loop to capture the value and use that instead of `i`. – juharr May 11 '16 at 13:19

4 Answers4

4

It is closure when you do:

(() => i)

The lambda gets the original variable i, not a copy, so you get 10 ten times - because on calling delegate the original value of i is 10 (after loop)

If you change code and add local temp variable you will get 0 1 2 3 4 5 6 7 8 9:

 for (int i = 0; i < 10; i++)
 {
     int j = i;
     list.Add(() => j);
 }

You can read about closures here: Closures

Community
  • 1
  • 1
Roman
  • 11,966
  • 10
  • 38
  • 47
1

The reason for that is that the lambda () => i captures the local variable i. This means that i will not be evaluated when it is added to the list but when you actually invoke the lambda with ().

At the time this happens in your code (Console.WriteLine(func());) the value of i is already 10 because the for loop has finished.

If you want to avoid this behaviour you have to copy the value of i into a local variable that will not change after the lambda has been created.

for (int i = 0; i < 10; i++)
{
     int tmp = i;
     list.Add(() => tmp);
}
Good Night Nerd Pride
  • 8,245
  • 4
  • 49
  • 65
1

When you pass variable inside of delegate's method it is its link, and not it's value that is used inside the delegate.

We create list of functions:

 List<Func<int>> list = new List<Func<int>>(); 

Here we initialize list with functions and every function should use reference to memory where i variable is stored when it's fired :

 for (int i = 0; i < 10; i++)
 {
     list.Add(() => i);
 }

Now it is time to fire each function but at this time for loop is already finished executing and i variable holds its final value of 10. Remember that delegate can always find parameter because it holds reference to it. It could not be garbage collected :

 foreach (var func in list)
 {
      // by the time we do it it has value of 10
      Console.WriteLine(func());
 }
Fabjan
  • 13,506
  • 4
  • 25
  • 52
0

Actually the lambda expression is a delegate, and you calling it after the loop ends and at that time i has value 10, so when the delegates are getting called, they all have i value 10 as same copy is used due to closure, you will need to write as @Roma suggested for it to work as you expect it to, otherwise it will,

this :

for (int i = 0; i < 10; i++)
 {
     list.Add(() => i);
 }

can be looked as :

 int i;
 for (i=0; i < 10; i++)
 {
     list.Add(() => i);
 }
Ehsan Sajjad
  • 61,834
  • 16
  • 105
  • 160