我不知道为什么我要用这段代码得到System.IndexOutOfRangeException: 'Index was outside the bounds of the array.'
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);
}请有人解释一下我的密码出了什么问题。
发布于 2018-09-30 06:52:29
问题是您的lambda表达式正在捕获变量i,但是委托直到循环之后才会执行。执行表达式c != illegals[i]时,i是illegals.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表达式之外提取非法
提取循环中的illegals[i]值(在lambda表达式之外),并在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);
}Except 选项4:使用 once
LINQ提供了一种执行集排除的方法:Except。但是,这与前面的选项不太一样,因为输出中只有一个特定字符的副本。因此,如果e不在illegals中,您将得到使用上述选项的"Tex resul“的结果,而使用Except的则是"Tex rsul”。不过,值得知道的是:
// Replace the loop entirely with this
query = query.Except(illegals);选项5:使用 once
您可以调用Where一次,使用一个调用Contains的lambda表达式
// Replace the loop entirely with this
query = query.Where(c => !illegals.Contains(c));发布于 2018-09-30 07:04:15
这是因为,虽然乍一看,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);https://stackoverflow.com/questions/52575358
复制相似问题