.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.Condition
是bool
,我使用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));