使用 LINQ Where 在 foreach 循环中删除元素

12

我能理解为什么这是不被允许的:

foreach (Thing t in myCollection) {
   if (shouldDelete(t) {
      myCollection.Delete(t);
   }
}

但这个怎么样呢?
foreach (Thing t in myCollection.Where(o=>shouldDelete(o)) {
   myCollection.Delete(t);
}

我不明白为什么会失败。显然,“Where()”方法并没有返回原始集合,因此当我尝试从中删除某些内容时,我没有枚举原始集合。

有一件事我不太明白关于这个“Thing”东西。它肯定不是内置类型的东西,对吧?有人能给我解释一下吗? - Deb
抱歉如果问题有些混淆。Thing可以是任何类,而myCollection是任何ICollection<Thing>。顺便说一句,Eric已经完全理解并回答了我的原始问题,所以我认为它已经关闭了。 - Andy
6个回答

26

我不明白为什么这个失败了。

那么你的问题应该是“为什么它失败了?”(在你的问题中,你忘记实际提问了。)

“Where()”方法显然没有返回原始集合。

正确。 "Where" 返回一个 IEnumerable<T>,表示加上筛选器后的集合。

所以当我试图从中删除一些内容时,我没有枚举到原始集合。

不正确。你正在枚举原始集合。你正在枚举带有筛选器的原始集合。

当你调用“Where”时,它并不会急切地评估筛选器并产生一个带有筛选器的全新副本,该副本已经应用于原始集合。相反,它会给你一个对象,该对象枚举原始集合,但跳过不符合筛选条件的项。

当你在商店里说“给我看所有的东西”时,向你展示所有的东西。当你说“现在只显示每公斤1美元到5美元之间的苹果”,你并没有构造出一个完全新的仅包含苹果的商店。你只是在查看之前完全相同的东西集合,只是加上了筛选器。


13
请尝试使用这段代码。
myCollection.RemoveAll(x => x.shouldDelete(x));

9

您可以做以下事情:

myCollection.RemoveAll(shouldDelete);

谢谢,实际上我想在循环内做其他事情,所以Likurg的建议对我更好,但我已经给你点赞了 :-) - Andy
2
如果myCollection的类型具有RemoveAll方法,则可以使用它! List<T>具有该方法,但许多集合类型则没有。 - phoog

6
第二个语句返回一个作用于你的列表上的 IEnumerable<>。 这个应该没问题:
foreach (Thing t in myCollection.Where(o=>shouldDelete(o).ToList()) {
   myCollection.Delete(t);
}

谢谢,这是我实施的解决方案,但我标记了Eric作为答案,因为我真正想知道的是底层发生了什么。 - Andy

2

这是因为在foreach循环内部不应该修改集合。它试图在整个foreach循环执行之前删除它。因此,它将失败。


1
我认为你的答案的第二部分很接近了... 你是在寻找惰性评估吗? - Joshua Drake

1

扩展方法基于传递的谓词过滤集合值并返回IEnumerable。因此,在迭代时无法修改集合。

您可以使用RemoveAll()来实现您的目的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接