Linq functions such as Where
create an IEnumerable that will be lazily evaluated.
This means the result of the Where
function will not be produced when the Where
function is being invoked. The result of the Where
function is being produced when the IEnumerable returned by Where
is being iterated.
In your example code, the results of the Where
functions are iterated in the foreach loop at the end of your code:
foreach (char c in charQuery1) Console.Write(c);
This is where the enumerabled returned by the Where
functions are being iterated, causing the predicate delegates provided to the Where
functions to be executed.
In your case, those predicate delegate are anonymous functions in the form of c => c != vowels[i]
, which close over the variable i
.
Once again, those anonymous functions c => c != vowels[i]
are invoked only when the
foreach loop at the end of your code is being executed.
So, what is the value of i
when the foreach loop is being executed? It's the last value it has when the for
loop exits. In other words, the value of i
is equal to vowels.Length
.
That leads to the predicate delegate to be equivalent to c => c != vowels[vowels.Length]
. Obviously, vowels[vowels.Length]
will cause an IndexOutOfRangeException.
How to fix it?
The naive approach would be to not let your predicates close over the i
counter variable of the for loop. Instead, let it close over a variable that is scoped inside the for body:
for (int i = 0; i < vowels.Length; i++)
{
var local_i = i;
charQuery1 = charQuery1.Where(c => c != vowels[local_i]);
}
But as the comment by Tim Schmelter points out, the more elegant and much shorter solution would be to simply use Linq's Except
method:
var charQueryWithoutVowels = charQuery1.Except(vowels);