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

时间:2011-01-17 22:47:01

标签: c# .net

我经常需要这样的东西:

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

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

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

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);
}

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

有更好的方法吗?

编辑:

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

例如

  

System.Windows.Controls.UIElementCollection

编辑2:

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

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

4 个答案:

答案 0 :(得分:17)

直接烘焙到List<T>

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

或在C#2.0中:

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

在非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);
        }
    }
}

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

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

建立一个新的列表instaed:

public IList<Line> GetListWithoutFullfilsCertainConditions(IList<Line> fullList) 
{
    IList<Line> resultList = new List<Line>(fullList.Count);

    foreach (Line line in fullList)
    {
       if (!line.FullfilsCertainConditions())
       {
          resultList.Add(line)
       }
    }

    return resultList;
}

答案 3 :(得分:1)

你也可以使用while循环。

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