Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IEnumerable IndexOutOfRangeException

Tags:

c#

ienumerable

I dont know why this I'm getting System.IndexOutOfRangeException: 'Index was outside the bounds of the array.' with this code

IEnumerable<char> query = "Text result";
string illegals = "abcet";

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

foreach (var item in query)
{
    Console.Write(item);
}

Please can someone explain what's wrong with my code.

like image 900
user2848242 Avatar asked Dec 18 '22 20:12

user2848242


1 Answers

The problem is that your lambda expression is capturing the variable i, but the delegate isn't being executed until after the loop. By the time the expression c != illegals[i] is executed, i is illegals.Length, because that's the final value of i. It's important to understand that lambda expressions capture variables, rather than "the values of those variables at the point of the lambda expression being converted into a delegate".

Here are five ways of fixing your code:

Option 1: local copy of i

Copy the value of i into a local variable within the loop, so that each iteration of the loop captures a new variable in the lambda expression. That new variable isn't changed by the rest of the execution of the loop.

for (int i = 0; i < illegals.Length; i++)
{
    int copy = i;
    query = query.Where(c => c != illegals[copy]);
}

Option 2: extract illegals[i] outside the lambda expression

Extract the value of illegals[i] in the loop (outside the lambda expression) and use that value in the lambda expression. Again, the changing value of i doesn't affect the variable.

for (int i = 0; i < illegals.Length; i++)
{
    char illegal = illegals[i];
    query = query.Where(c => c != illegal);
}

Option 3: use a foreach loop

This option only works properly with C# 5 and later compilers, as the meaning of foreach changed (for the better) in C# 5.

foreach (char illegal in illegals)
{
    query = query.Where(c => c != illegal);
}

Option 4: use Except once

LINQ provides a method to perform set exclusion: Except. This is not quite the same as the earlier options though, as you'll only get a single copy of any particular character in your output. So if e wasn't in illegals, you'd get a result of "Tex resul" with the above options, but "Tex rsul" using Except. Still, it's worth knowing about:

// Replace the loop entirely with this
query = query.Except(illegals);

Option 5: Use Contains once

You can call Where once, with a lambda expression that calls Contains:

// Replace the loop entirely with this
query = query.Where(c => !illegals.Contains(c));
like image 89
Jon Skeet Avatar answered Jan 04 '23 14:01

Jon Skeet