我的代码想要看起来像这样:
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
丑陋,但它应该有用。
答案 0 :(得分:58)
您可以向后遍历列表:
for (int i = myList.Count - 1; i >= 0; i--)
{
if (whatever) myList.RemoveAt(i);
}
当你找到一个你没有删除的项目时想要退出的评论,那么只使用while循环将是最好的解决方案。
答案 1 :(得分:55)
在foreach循环内部,你永远不应该从正在迭代的集合中删除任何东西。它基本上就像锯你所坐的分支一样。
使用你的替代选择。这是要走的路。
答案 2 :(得分:30)
您真的需要在foreach
循环中执行此操作吗?
这将获得与您的示例相同的结果,即从列表中删除所有项目,直到匹配条件的第一个项目(或删除所有项目,如果它们都不匹配条件)。
int index = Os.FindIndex(x => x.cond);
if (index > 0)
Os.RemoveRange(0, index);
else if (index == -1)
Os.Clear();
答案 3 :(得分:15)
我是一名Java程序员,但这样的工作原理:
List<Type> Os;
List<Type> Temp;
...
foreach (Type o in Os)
if (o.cond)
Temp.add(o);
Os.removeAll(Temp);
答案 4 :(得分:13)
我的分析库遇到了这个问题。我试过这个:
for (int i = 0; i < list.Count; i++)
{
if (/*condition*/)
{
list.RemoveAt(i);
i--;
}
}
这很简单,但我没有想到任何突破点。
答案 5 :(得分:12)
这是最轻松的解决方案,最简单的为什么
通常,我们从原始列表中删除,这会产生维护列表计数和迭代器位置的问题。
List<Type> Os = ....;
Os.ForEach(
delegate(Type o) {
if(!o.cond) Os.Remove(o);
}
);
LINQ.ForEach
:请注意我添加的所有内容都是ToList()
。这将创建一个您执行ForEach的新列表,因此您可以删除原始列表,但继续遍历整个列表。
List<Type> Os = ....;
Os.ToList().ForEach(
delegate(Type o) {
if(!o.cond) Os.Remove(o);
}
);
foreach
:此技术也适用于常规 foreach
语句。
List<Type> Os = ....;
foreach(Type o in Os.ToList()) {
if(!o.cond) Os.Remove(o);
}
请注意,如果您的原始列表包含struct
元素,则此解决方案将无效。
答案 6 :(得分:11)
我知道你要求别的东西,但是如果你想有条件地删除一堆元素你可以使用lambda表达式:
Os.RemoveAll(o => !o.cond);
答案 7 :(得分:9)
Os.RemoveAll(delegate(int x) { return /// });
答案 8 :(得分:4)
我尝试找到不满足谓词的第一项的索引,并对其执行RemoveRange(0,index)。如果不出意外,应该减少删除电话。
答案 9 :(得分:3)
更新:添加完整性
正如一些人已经回答的那样,你不应该在使用GetEnumerator()(例如foreach
)进行迭代时修改集合。该框架阻止您通过抛出异常来做到这一点。对此的通用判断是使用for
“手动”迭代(参见其他答案)。请注意索引,这样就不会跳过项目或重新评估相同的项目(使用i--
或向后迭代)。
但是,对于您的具体情况,我们可以优化删除操作...原始答案如下。
如果你想要的是删除所有项目,直到满足给定条件(这是你的代码所做的),你可以这样做:
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
来保存退出条件。
更新:添加边界检查并将退出条件保存到两个示例
答案 10 :(得分:2)
你可以用linq
来做MyList = MyList.Where(x=>(someCondition(x)==true)).ToList()
答案 11 :(得分:1)
如果您知道列表不是很大,可以使用
foreach (Type o in new List<Type>(Os))
....
将创建列表的临时副本。你的remove()调用将不会干扰迭代器。
答案 12 :(得分:1)
在 Removing items in a list while iterating through it 中对此进行了很好的讨论。
他们建议:
for(int i = 0; i < count; i++)
{
int elementToRemove = list.Find(<Predicate to find the element>);
list.Remove(elementToRemove);
}
答案 13 :(得分:1)
查看Enumerable.SkipWhile()
Enumerable.SkipWhile( x => condition).ToList()
一般不会改变列表,让生活更轻松。 :)
答案 14 :(得分:1)
Anzurio的解决方案可能是最直接的解决方案,但如果您不介意在实用程序库中添加一堆接口/类,那么这是另一个简洁的解决方案。
你可以像这样写
List<Type> Os;
...
var en = Os.GetRemovableEnumerator();
while (en.MoveNext())
{
if (en.Current.Cond)
en.Remove();
}
将受Java's 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--;
}
}
答案 15 :(得分:0)
我遇到了同样的问题并使用以下方法解决了这个问题:
foreach (Type o in (new List(Os)))
{
if (something)
Os.Remove(o);
}
它遍历列表的副本并从原始列表中删除。
答案 16 :(得分:0)
在列表中添加要删除的项目,然后使用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));