Different results between returns and LINQ choices

I always thought these two methods were similar:

public static IEnumerable<Func<int>> GetFunctions() { for(int i = 1; i <= 10; i++) yield return new Func<int>(() => i); } public static IEnumerable<Func<int>> GetFunctionsLinq() { return Enumerable.Range(1, 10).Select(i => new Func<int>(() => i)); } 

However, when converting them to List<Func<int>> they give different results:

 List<Func<int>> yieldList = GetFunctions().ToList(); List<Func<int>> linqList = GetFunctionsLinq().ToList(); foreach(var func in yieldList) Console.WriteLine("[YIELD] {0}", func()); Console.WriteLine("=================="); foreach(var func in linqList) Console.WriteLine("[LINQ] {0}", func()); 

Output:

 [YIELD] 11 [YIELD] 11 [YIELD] 11 [YIELD] 11 [YIELD] 11 [YIELD] 11 [YIELD] 11 [YIELD] 11 [YIELD] 11 [YIELD] 11 ================== [LINQ] 1 [LINQ] 2 [LINQ] 3 [LINQ] 4 [LINQ] 5 [LINQ] 6 [LINQ] 7 [LINQ] 8 [LINQ] 9 [LINQ] 10 

Why is this?

+6
source share
2 answers

This problem is closing. You must save the variable inside the loop to solve this problem.

 for (int i = 1; i <= 10; i++) { var i1 = i; yield return new Func<int>(() => i1); } 

Actually new Func<int>(() => i); uses the exact counter value inside the loop, not a copy. Therefore, after completing the cycle, you always get 11 , because this is the last value set for the counter.

+6
source

i in for(int i = 1; i <= 10; i++) is the same variable in each loop, just changing the value.

() => i is a closure. When it is called, it uses the current value of i , not the value that i had when creating Func .

First, you list GetFunctions before calling the returned functions, so i already 11 in each of them.

If you call functions immediately after receiving them from the counter, you will get the same results as in the LINQ version:

 foreach (var f in GetFunctions()) Console.WriteLine("[YIELD2] {0}", f()); 

In any case, it is not recommended to create closures on loop variables.

+1
source

All Articles