IEnumerable IndexOutOfRangeException

时间:2018-09-30 06:48:04

标签: c# ienumerable

我不知道为什么我用这段代码得到pdCut(Seq(3,6,4,1,9,5), Seq(3,5)) //res0: Seq[(String, Seq[Int])] = Seq((@-3,Seq(-2147483648, 3)), (5-@,Seq(5, 2147483647)), (3-5,Seq(3, 5)), (@-3,Seq(-2147483648, 3)), (5-@,Seq(5, 2147483647)), (3-5,Seq(3, 5)))

System.IndexOutOfRangeException: 'Index was outside the bounds of the array.'

请有人可以解释我的代码有什么问题。

2 个答案:

答案 0 :(得分:11)

问题在于您的lambda表达式正在捕获变量i,但是直到循环之后,委托才被执行。在执行表达式c != illegals[i]时,iillegals.Length,因为这是i的最终值。重要的是要理解lambda表达式捕获变量,而不是“在lambda表达式被转换为委托时这些变量的值”。

这里有五种修复代码的方法:

选项1:i的本地副本

i的值复制到循环内的局部变量中,以便循环的每次迭代都在lambda表达式中捕获新变量。该新变量不会在循环的其余执行过程中更改。

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

选项2:在lambda表达式之外提取非法内容[i]

在循环中(在lambda表达式之外)提取illegals[i]的值,并在lambda表达式中使用该值。同样,i的变化值不会影响该变量。

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

选项3:使用foreach循环

此选项仅适用于C#5和更高版本的编译器,因为foreach的含义在C#5中有所改变(更好)。

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

选项4:一次使用Except

LINQ提供了一种执行集合排除的方法:Except。不过,这与以前的选项不是完全相同,因为您只会在输出中得到任何特定字符的单个副本。因此,如果e不在illegals中,则使用上述选项会得到“ Tex resul”的结果,而使用Except会得到“ Tex rsul”的结果。尽管如此,还是值得了解的:

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

选项5:一次使用Contains

您可以使用调用Where的lambda表达式调用一次Contains

// Replace the loop entirely with this
query = query.Where(c => !illegals.Contains(c));

答案 1 :(得分:0)

之所以会这样,是因为尽管乍一看您的for循环似乎已正确界定边界,但每次迭代都会捕获传递给Where的闭包中的索引。闭包最有用的属性之一是它们通过引用捕获,从而启用了各种强大而复杂的技术。但是,在这种情况下,这意味着,在随后的foreach循环中执行查询时。索引已增加超过数组的长度。

解决此问题的最直接的更改是创建一个循环范围的副本,复制索引循环控制变量的当前值,并在您的闭包中引用它,而不是直接引用循环控制变量。

例如:

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

但是,正如其他人所指出的那样,有更好的书写方式可以完全解决问题,并且它们还具有提高抽象水平的优点。

例如,您可以使用System.Linq.Enumerable.Except

var legals = query.Except(illegals);