.NET – 从“foreach”循环中的列表<T>中删除

我有我想要看起来像这样的代码:

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循环中时,你不能从列表中删除:

有没有一种常见的方法来解决这个问题?

如果需要,我可以切换到不同的types。

选项2:

 List<Type> Os; ... while (Os.Count != 0) if (Os[0].cond) return; else Os.RemoveAt(0); ... // Other code 

丑,但它应该工作。

你真的需要在foreach循环中做到这一点?

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

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

您可以向后遍历列表:

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

为了回应你的意见,当你find一个你没有删除的项目时想退出,那么使用while循环将是最好的解决scheme。

在foreach循环中,你永远不应该从你正在迭代的集合中删除任何东西。 这基本上就像锯你正在坐的分支。

使用你的替代品。 这是要走的路。

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

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

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

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

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

这里是最简单WHY简单的解决scheme

问题:

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

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

解决scheme – LINQ.ForEach

注意我所添加的是ToList() 。 这将创build一个新的列表,您执行ForEach,因此您可以删除您的原始列表,但不断遍历整个列表。

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

解决scheme – 定期的foreach

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

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

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

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

 Os.RemoveAll(o => !o.cond); 
  Os.RemoveAll(delegate(int x) { return /// }); 

我会尝试find不满足谓词的第一个项目的索引,并对其执行RemoveRange(0,索引)。 如果没有别的,应该有更less的删除电话。

更新:为了完整而添加

正如几个人已经回答,你不应该修改一个集合,同时使用GetEnumerator()(例如foreach )迭代它。 该框架通过抛出exception来防止你这样做。 对此的通用colution是用“ for ”手动迭代(请参阅其他答案)。 小心你的索引,所以你不要跳过项目或重新评估相同的两次(通过使用i--或反向迭代)。

但是,对于您的具体情况,我们可以优化删除操作(S)…原来的答案在下面。


如果你想要的是删除所有的项目,直到一个符合给定的条件(这是你的代码所做的),你可以这样做:

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

或者如果你想使用一个删除操作:

 SomeType exitCondition; int index = list.FindIndex(i => i.Condition); if(index < 0) list.Clear(); else { exitCondition = list[0].State; list.RemoveRange(0, count); } 

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

更新:添加边界检查并保存两个示例的退出条件

如果你知道你的名单不是很大,你可以使用

 foreach (Type o in new List<Type>(Os)) .... 

这将创build列表的临时副本。 你的remove()调用将不会干扰迭代器。

看看Enumerable.SkipWhile()

 Enumerable.SkipWhile( x => condition).ToList() 

一般不会改变列表,让生活更容易。 🙂

在迭代列表删除列表中的项目时,有一个很好的讨论。

他们build议:

 for(int i = 0; i < count; i++) { int elementToRemove = list.Find(<Predicate to find the element>); list.Remove(elementToRemove); } 

你可以用linq来做

 MyList = MyList.Where(x=>(someCondition(x)==true)).ToList() 

Anzurio的解决scheme可能是最简单的,但如果你不介意在你的公用程序库中添加一些接口/类,那么这里又是一个干净的解决scheme。

你可以这样写

 List<Type> Os; ... var en = Os.GetRemovableEnumerator(); while (en.MoveNext()) { if (en.Current.Cond) en.Remove(); } 

将Java的Iterator<T>.remove启发下的基础架构放到实用程序库中:

 static class Extensions { public static IRemovableEnumerator<T> GetRemovableEnumerator<T>(this IList<T> l) { return new ListRemovableEnumerator<T>(l); } } interface IRemovableEnumerator<T> : IEnumerator<T> { void Remove(); } class ListRemovableEnumerator<T> : IRemovableEnumerator<T> { private readonly IList<T> _list; private int _count; private int _index; public ListRemovableEnumerator(IList<T> list) { _list = list; _count = list.Count; _index = -1; } private void ThrowOnModification() { if (_list.Count != _count) throw new InvalidOperationException("List was modified after creation of enumerator"); } public void Dispose() { } public bool MoveNext() { ThrowOnModification(); if (_index + 1 == _count) return false; _index++; return true; } public void Reset() { ThrowOnModification(); _index = -1; } object IEnumerator.Current { get { return Current; } } public T Current { get { return _list[_index]; } } public void Remove() { ThrowOnModification(); _list.RemoveAt(_index); _index--; _count--; } } 

我只是有同样的问题,并通过使用以下解决scheme:

foreach (Type o in (new List(Os))) { if (something) Os.Remove(o); }

它遍历列表的副本并从原始列表中删除。

添加要在列表中删除的项目,然后通过使用RemoveAll删除这些项目:

 List<Type> Os; List<Type> OsToRemove=new List<Type>(); ... foreach (Type o in Os){ if (o.cond) return; else OsToRemove.Add(o); } Os.RemoveAll(o => OsToRemove.Contains(o));