避免InvalidOperationException的最佳做法:Collection被修改了?

我经常需要这样的东西:

foreach (Line line in lines) { if (line.FullfilsCertainConditions()) { lines.Remove(line) } } 

这不起作用,因为我总是得到一个InvalidOperationException因为在循环期间更改了枚举器。

所以我将所有这种循环改为以下内容:

 List remove = new List(); foreach (Line line in lines) { if (line.FullfilsCertainConditions()) { remove.Add(line) } } foreach (Line line in remove) { { lines.Remove(line); } 

我不确定这是否真的是最好的方式,因为在最坏的情况下我必须在原始列表上迭代2次,所以它需要时间2n而不是n。

有一个更好的方法吗?

编辑:

我能够使用Mark的答案来做到这一点!但是如果我的集合没有实现RemoveAll()怎么办?

例如a

System.Windows.Controls.UIElementCollection

编辑2:

再次在Mark的帮助下,我现在能够进行以下调用以删除所有ScatterViewItems:

 CollectionUtils.RemoveAll(manager.getWindow().IconDisplay.Items, elem => elem.GetType() == typeof(ScatterViewItem)); 

这直接烘焙到List

 lines.RemoveAll(line => line.FullfilsCertainConditions()); 

或者在C#2.0中:

 lines.RemoveAll(delegate(Line line) { return line.FullfilsCertainConditions(); }); 

在非List情况下(您对问题的编辑),您可以将此包装为如下(未经测试):

 static class CollectionUtils { public static void RemoveAll(IList list, Predicate predicate) { int count = list.Count; while (count-- > 0) { if (predicate(list[count])) list.RemoveAt(count); } } public static void RemoveAll(IList list, Predicate predicate) { int count = list.Count; while (count-- > 0) { if (predicate(list[count])) list.RemoveAt(count); } } } 

由于UIElementCollection实现了(非generics) IList因此应该可行。 而且非常方便,使用C#3.0,您可以在IList / IList之前添加一个,并将其作为扩展方法。 唯一的微妙之处在于anon-method的参数将是object ,因此您需要将其抛弃。

您只需将原始列表替换为已过滤的列表:

 lines = lines.Where(line => line.FullfilsCertainConditions()).ToList(); 

建立一个新的列表instaed:

 public IList GetListWithoutFullfilsCertainConditions(IList fullList) { IList resultList = new List(fullList.Count); foreach (Line line in fullList) { if (!line.FullfilsCertainConditions()) { resultList.Add(line) } } return resultList; } 

你也可以使用while循环。

 int i = 0; while(i < lines.Count) { if (lines[i].FullfilsCertainConditions()) { lines.RemoveAt(i); } else {i++;} }