.NET - 从“foreach”循环内的 List<T> 中删除

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/832165/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 12:38:39  来源:igfitidea点击:

.NET - Remove from a List<T> within a 'foreach' loop

.netforeachremove-method

提问by BCS

I have code that I wantto 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 foreachloop over that list:

这不起作用,因为当您处于该列表的foreach循环内时,您无法从列表中删除:

Is there a common way to solve the problem?

有没有解决问题的通用方法?

I can switch to a different type if needed.

如果需要,我可以切换到其他类型。

Option 2:

选项 2:

List<Type> Os;

...

while (Os.Count != 0)
     if (Os[0].cond)
         return;
     else
         Os.RemoveAt(0);

... // Other code

Ugly, but it should work.

丑陋,但它应该工作。

采纳答案by LukeH

Do you really need to do this within a foreachloop?

您真的需要在foreach循环中执行此操作吗?

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).

这将获得与您的示例相同的结果,即,从列表中删除所有项目,直到第一个匹配条件的项目(如果没有一个项目与条件匹配,则删除所有项目)。

int index = Os.FindIndex(x => x.cond);

if (index > 0)
    Os.RemoveRange(0, index);
else if (index == -1)
    Os.Clear();

回答by Jon B

You can iterate through the list backwards:

您可以向后遍历列表:

for (int i = myList.Count - 1; i >= 0; i--)
{
    if (whatever) myList.RemoveAt(i);
}


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.

为了回应您关于在发现您没有删除的项目时想要退出的评论,那么仅使用 while 循环将是最佳解决方案。

回答by User

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.

在 foreach 循环中,您永远不应该从正在迭代的集合中删除任何内容。这基本上就像锯你所坐的树枝一样。

Use your while alternative. It is the way to go.

使用您的 while 替代方案。这是要走的路。

回答by Milhous

I am a Java programmer, but something like this works:

我是一名 Java 程序员,但像这样的工作:

List<Type> Os;
List<Type> Temp;
...
foreach (Type o in Os)
    if (o.cond)
        Temp.add(o);
Os.removeAll(Temp);  

回答by Anzurio

I just had that problem with my analysis library. I tried this:

我的分析库刚刚遇到了这个问题。我试过这个:

for (int i = 0; i < list.Count; i++)
{                
   if (/*condition*/)
   {
       list.RemoveAt(i);
       i--;
   }
}

It's pretty simple but I haven't thought of any breaking point.

这很简单,但我没有想到任何突破点。

回答by sonjz

Here is the EASIEST SOLUTIONwith the simpliest WHY

这是最简单的解决方案和最简单的原因

PROBLEM:

问题:

Typically, we are removing from the original list, this produces the problem of maintaining the list count and iterator location.

通常,我们从原始列表中删除,这会产生维护列表计数和迭代器位置的问题。

List<Type> Os = ....;
Os.ForEach(
    delegate(Type o) {
        if(!o.cond) Os.Remove(o);
    }
);

SOLUTION - LINQ.ForEach:

解决方案 - 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.

请注意我添加的所有内容是ToList(). 这将创建一个您对其执行 ForEach 的新列表,因此您可以删除原始列表,但继续遍历整个列表。

List<Type> Os = ....;
Os.ToList().ForEach(
    delegate(Type o) {
        if(!o.cond) Os.Remove(o);
    }
);

SOLUTION - Regular foreach:

解决方案 - 常规foreach

This technique also works for regular foreachstatements.

这种技术也适用于常规foreach语句。

List<Type> Os = ....;
foreach(Type o in Os.ToList()) {
  if(!o.cond) Os.Remove(o);
}

Please note, that this solution won't work if your original List contains structelement.

请注意,如果您的原始 List 包含struct元素,则此解决方案将不起作用。

回答by Day_Dreamer

I know you asked for something else, but if you want to conditionally remove a bunch of elements you can use lambda expression:

我知道你要求别的东西,但如果你想有条件地删除一堆元素,你可以使用 lambda 表达式:

Os.RemoveAll(o => !o.cond);

回答by dirkgently

 Os.RemoveAll(delegate(int x) { return /// });

回答by bh213

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.

我会尝试找到不满足谓词的第一项的索引,并对其执行 RemoveRange(0, index) 。如果不出意外,应该减少 Remove 调用。

回答by Lucas

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" with for(see other answers). Be careful with your index so you don't skip items or re-evaluate the same one twice (by using i--or iterating backward).

正如一些人所回答的那样,您不应该在使用 GetEnumerator() 迭代集合时修改它(示例foreach)。该框架通过抛出异常来阻止您执行此操作。对此的通用解决方案是“手动”迭代for(参见其他答案)。小心你的索引,这样你就不会跳过项目或重新评估相同的项目两次(通过使用i--或向后迭代)。

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:

如果您想要删除所有项目直到满足给定条件(这就是您的代码所做的),您可以这样做:

bool exitCondition;

while(list.Count > 0 && !(exitCondition = list[0].Condition))
   list.RemoveAt(0);

Or if you want to use a single remove operation:

或者,如果您想使用单个删除操作:

SomeType exitCondition;
int index = list.FindIndex(i => i.Condition);

if(index < 0)
    list.Clear();
else
{
    exitCondition = list[0].State;
    list.RemoveRange(0, count);
}

Note: since I'm assuming that item.Conditionis bool, I'm using item.Stateto save the exit condition.

注意:因为我假设item.Conditionbool,所以我使用item.State来保存退出条件。

Update: added bounds checking and saving exit condition to both examples

更新:为两个示例添加了边界检查和保存退出条件