.NET - 从列表中删除在“foreach”内 环形
我希望代码看起来像这样:
List<Type> Os;
...
foreach (Type o in Os)
if (o.cond)
return; // Quitting early is important for my case!
else
Os.Remove(o);
... // Other code
这不起作用,因为当您处于该列表的 foreach
循环内时,无法从列表中删除:
是有没有通用的方法来解决问题?
如果需要的话我可以切换到不同的类型。
选项2:
List<Type> Os;
...
while (Os.Count != 0)
if (Os[0].cond)
return;
else
Os.RemoveAt(0);
... // Other code
丑陋,但应该有用。
I have code that I want to look like this:
List<Type> Os;
...
foreach (Type o in Os)
if (o.cond)
return; // Quitting early is important for my case!
else
Os.Remove(o);
... // Other code
This doesn't work, because you cannot remove from the list when you are inside a foreach
loop over that list:
Is there a common way to solve the problem?
I can switch to a different type if needed.
Option 2:
List<Type> Os;
...
while (Os.Count != 0)
if (Os[0].cond)
return;
else
Os.RemoveAt(0);
... // Other code
Ugly, but it should work.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(17)
您可以向后迭代列表:
为了响应您关于在找到不删除的项目时想要退出的评论,那么仅使用 while 循环将是最好的解决方案。
You can iterate through the list backwards:
In response to your comment about wanting to quit when you find an item that you're NOT removing, then just using a while loop would be the best solution.
在 foreach 循环内部时,永远不应该从正在迭代的集合中删除任何内容。 这基本上就像锯你坐的树枝一样。
使用您的 while 替代方案。 这是必经之路。
You should never remove anything from a collection you are iterating over while inside of a foreach loop. It's basically like sawing the branch you are sitting on.
Use your while alternative. It is the way to go.
您真的需要在 foreach 循环中执行此操作吗?
这将实现与示例相同的结果,即从列表中删除所有项目,直到第一个与条件匹配的项目为止(或者如果没有一个项目与条件匹配,则删除所有项目)。
Do you really need to do this within a
foreach
loop?This will achieve the same results as your examples, ie, remove all items from the list up until the first item that matches the condition (or remove all items if none of them match the condition).
我是一名 Java 程序员,但是这样的事情是有效的:
I am a Java programmer, but something like this works:
我的分析库刚刚遇到了这个问题。 我尝试过:
这很简单,但我没有想到任何突破点。
I just had that problem with my analysis library. I tried this:
It's pretty simple but I haven't thought of any breaking point.
这是最简单的解决方案,具有最简单的原因
问题:
通常,我们从原始列表中删除,这会产生维护列表计数和迭代器位置的问题。
解决方案 -
LINQ.ForEach
:请注意,我添加的只是
ToList()
。 这将创建一个新列表,您可以对其执行 ForEach,因此您可以删除原始列表,但继续迭代整个列表。解决方案 - 常规
foreach
:此技术也适用于常规
foreach
语句。请注意,如果您的原始列表包含
struct
元素,则此解决方案将不起作用。Here is the EASIEST SOLUTION with the simpliest WHY
PROBLEM:
Typically, we are removing from the original list, this produces the problem of maintaining the list count and iterator location.
SOLUTION -
LINQ.ForEach
:Note all I've added was
ToList()
. This creates a new list that you perform ForEach on, therefore you can remove your original list, yet keep iterating through the entire list.SOLUTION - Regular
foreach
:This technique also works for regular
foreach
statements.Please note, that this solution won't work if your original List contains
struct
element.我知道您还要求其他东西,但如果您想有条件地删除一堆元素,您可以使用 lambda 表达式:
I know you asked for something else, but if you want to conditionally remove a bunch of elements you can use lambda expression:
我会尝试找到不满足谓词的第一个项目的索引,并对其执行RemoveRange(0,index)。 如果不出意外,Remove 调用应该会减少。
I'd try finding the index of first item that does not satisfy the predicate and do RemoveRange(0, index) on it. If nothing else, there should be less Remove calls.
更新:为了完整性而添加
正如一些人已经回答的那样,您不应在使用 GetEnumerator() 迭代集合时修改集合(例如
foreach
)。 框架通过抛出异常来阻止您执行此操作。 对此的通用解决方案是使用for
“手动”迭代(请参阅其他答案)。 请小心索引,以免跳过项目或对同一项目重新计算两次(通过使用i--
或向后迭代)。但是,对于您的具体情况,我们可以优化删除操作...下面的原始答案。
如果您想要删除所有项目,直到其中一个项目满足给定条件(这就是您的代码所做的),您可以执行以下操作:
或者如果您想使用单个删除操作:
注意:因为我假设
item.Condition 是
bool
,我使用item.State
来保存退出条件。更新:为两个示例添加了边界检查和保存退出条件
Update: Added for completeness
As several have answered, you shouldn't modify a collection while iterating it with GetEnumerator() (example
foreach
). The framework prevent you from doing this by throwing an exception. The generic colution to this is to iterate "manually" withfor
(see other answers). Be careful with your index so you don't skip items or re-evaluate the same one twice (by usingi--
or iterating backward).However, for your specific case, we can optimize the remove operation(s)... original answer below.
If what you want is to remove all items until one meets a given condition (that's what your code does), you can do this:
Or if you want to use a single remove operation:
Note: since I'm assuming that
item.Condition
isbool
, I'm usingitem.State
to save the exit condition.Update: added bounds checking and saving exit condition to both examples
你可以用 linq 来做
you can do it with linq
如果您知道您的列表不是很大,您可以使用
它将创建列表的临时副本。 这样,您的remove() 调用将不会干扰迭代器。
If you know your list isn't very large you can use
which will create a temporary duplicate of the list. Your remove() call will then not be interfering with the iterator.
看看
Enumerable.SkipWhile()
通常不会改变列表,使生活变得更容易。 :)
Look at
Enumerable.SkipWhile()
Generally not mutating a list, makes live a lot easier. :)
在迭代列表时删除列表中的项目。
他们建议:
There is a good discussion of this in Removing items in a list while iterating through it .
They propose:
Anzurio 的解决方案可能是最简单的,但如果您不介意向实用程序库添加一堆接口/类,这里还有另一个干净的解决方案。
您可以这样写,
放置以下基础设施,灵感来自 Java 的
Iterator.remove
到您的实用程序库中:Anzurio's solution is probably the most straightforward, but here's another clean one, if you don't mind adding a bunch of interfaces/classes to your utilities library.
You can write it like this
Put the following infrastructure, inspired by Java's
Iterator<T>.remove
, into your utility library:我刚刚遇到了同样的问题,并使用以下方法解决了它:
foreach (Type o in (new List(Os)))
{
如果(某事)
Os.Remove(o);
它迭代
列表的副本并从原始列表中删除。
I just had the same problem and solved it by using the following:
foreach (Type o in (new List(Os)))
{
if (something)
Os.Remove(o);
}
It iterates through a copy of the list and removes from the original list.
在列表中添加要删除的项目,然后使用
RemoveAll
删除这些项目:Add the item to remove in a list, and then remove these items by using
RemoveAll
: