1

I have two code looped snippets latter works as expected while the former throws exception. Why does foreach work while for loop doesn't? What does it stem from?

IEnumerable<char> query = "Not what you might expect";

query = query.Where (c => c != 'a');
query = query.Where (c => c != 'e');
query = query.Where (c => c != 'i');
query = query.Where (c => c != 'o');
query = query.Where (c => c != 'u');

foreach (char c in query) Console.Write (c);  // Nt wht y mght xpct

For loop with exception

IEnumerable<char> query = "Not what you might expect";
string vowels = "aeiou";

for (int i = 0; i < vowels.Length; i++)
  query = query.Where (c => c != vowels[i]);

foreach (char c in query) Console.Write (c);

foreach code,

foreach (char vowel in vowels)
  query = query.Where (c => c != vowel);

foreach (char c in query) Console.Write (c);
Pavel Anikhouski
  • 21,776
  • 12
  • 51
  • 66
  • 2
    https://blogs.msdn.microsoft.com/ericlippert/2009/11/12/closing-over-the-loop-variable-considered-harmful/ – Dave M Dec 08 '19 at 20:33

1 Answers1

2

Well, Linq uses lazy (deffered execution), so you have:

// Just a declaration, query doesn't execute
for (int i = 0; i < vowels.Length; i++)
  query = query.Where (c => c != vowels[i]);

// now, after the for loop, i is equal to vowels.Length

// Here, query executes with current i value, i == vowels.Length
// And you have an out of range exception
foreach (char c in query) Console.Write (c);
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
  • 1
    This answer doesn’t really explain why the two seemingly equivalent methods produce different results. The fact that execution is deferred is true for both examples. The real reason for the difference is that C# 5 took a breaking change to make the foreach loop not close over the loop variable, but left the for loop behavior the same. – Dave M Dec 08 '19 at 20:39
  • @DaveM as well as he is really experienced guy, his answer is right but if possible a bit more explanation would be better. It is exactly due to _lazy eval_ +1 – Soner from The Ottoman Empire Dec 08 '19 at 21:03
  • 2
    It has nothing to do with lazy eval. It has everything to do with how closures and variable capturing work in C#. – Dave M Dec 08 '19 at 21:35
  • @DaveM: it must have _something_ "to do with lazy eval", since if the `Where()` method invoked the delegate immediately when it's called, instead of lazily after the loop has completed, the issue would not exist. I don't see how you can say it literally has _nothing_ to do with lazy evaluation. – Peter Duniho Dec 09 '19 at 09:44