C# IEnumerable IndexOutOfRangeException

C# IEnumerable IndexOutOfRangeException,c#,ienumerable,C#,Ienumerable,我不知道为什么我会得到System.IndexOutOfRangeException:“索引超出了数组的界限。”使用此代码 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

我不知道为什么我会得到
System.IndexOutOfRangeException:“索引超出了数组的界限。”
使用此代码

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);
}
IEnumerable query=“文本结果”;
字符串非法者=“abcet”;
for(int i=0;ic!=非法者[i]);
}
foreach(查询中的var项)
{
控制台。写入(项);
}

请有人解释一下我的代码出了什么问题。

问题是lambda表达式正在捕获变量
i
,但直到循环结束后才执行委托。当表达式
c!=非法者[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]);
}
选项3:使用foreach循环

此选项仅适用于C#5及更高版本的编译器,因为在C#5中,
foreach
的含义发生了变化(更好)

选项4:使用
一次,但
除外

LINQ提供了一种执行集合排除的方法:
Except
。不过,这与前面的选项并不完全相同,因为您只能在输出中获得任何特定字符的单个副本。因此,如果
e
不在
illegals
中,您将使用上述选项获得“Tex result”的结果,但使用
的“Tex rsul”除外。不过,值得了解的是:

// Replace the loop entirely with this
query = query.Except(illegals);
选项5:使用
包含
一次

您可以使用调用
包含
的lambda表达式调用
其中的
一次:

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

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

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

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


最好这样:var query=query.Where(c=>!illegals.Contains(c));用它替换你的循环。@eocron:是的,在看到你的评论之前,我刚刚添加了它作为第五个选项:)
// Replace the loop entirely with this
query = query.Where(c => !illegals.Contains(c));
for (int i = 0; i < illegals.Length; i++)
{
    var index = i;
    query = query.Where(c => c != illegals[index]);
}
var legals = query.Except(illegals);