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

12 .net c#

我经常需要这样的东西:

 foreach (Line line in lines)
 {
    if (line.FullfilsCertainConditions())
    {
       lines.Remove(line)
    }
 }
Run Code Online (Sandbox Code Playgroud)

这不起作用,因为我总是得到一个InvalidOperationException因为Enumerator在循环期间被更改了.

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

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

foreach (Line line in remove) {
{
   lines.Remove(line);
}
Run Code Online (Sandbox Code Playgroud)

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

有一个更好的方法吗?

编辑:

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

例如a

System.Windows.Controls.UIElementCollection

编辑2:

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

CollectionUtils.RemoveAll(manager.getWindow().IconDisplay.Items, elem => elem.GetType() == typeof(ScatterViewItem));
Run Code Online (Sandbox Code Playgroud)

Mar*_*ell 17

这直接烘焙到List<T>:

lines.RemoveAll(line => line.FullfilsCertainConditions());
Run Code Online (Sandbox Code Playgroud)

或者在C#2.0中:

lines.RemoveAll(delegate(Line line) {
    return line.FullfilsCertainConditions();
});
Run Code Online (Sandbox Code Playgroud)

在非List<T>案例(您对问题的编辑)中,您可以将此内容包装在下面(未经测试):

static class CollectionUtils
{
    public static void RemoveAll<T>(IList<T> list, Predicate<T> predicate)
    {
        int count = list.Count;
        while (count-- > 0)
        {
            if (predicate(list[count])) list.RemoveAt(count);
        }
    }
    public static void RemoveAll(IList list, Predicate<object> predicate)
    {
        int count = list.Count;
        while (count-- > 0)
        {
            if (predicate(list[count])) list.RemoveAt(count);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

由于UIElementCollection实现(非泛型)IList这应该工作.而且比较方便,用C#3.0,你可以添加一个thisIList/ IList<T>并将它作为一个扩展方法.唯一的微妙之处在于anon-method的参数将是object,所以你需要将其抛弃.

  • @Matten - 添加了一个C#2.0示例. (2认同)