Подтвердить что ты не робот

Лучше всего избегать InvalidOperationException: коллекция была изменена?

Очень часто мне нужно что-то вроде этого:

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

Это не работает, потому что я всегда получаю 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);
}

Я не уверен, что это действительно лучший способ, так как в худшем случае я должен повторять 2 раза по сравнению с исходным списком, поэтому ему нужно время 2n вместо n.

Есть ли лучший способ сделать это?

EDIT:

Я смог сделать это, используя ответ Mark! Но что, если моя коллекция не реализует RemoveAll()?

Например, <

System.Windows.Controls.UIElementCollection

ИЗМЕНИТЬ 2:

Снова с помощью Mark я теперь могу сделать следующий вызов, чтобы удалить все ScatterViewItems:

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

Ответ 1

Это выпекается непосредственно в List<T>:

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

или в С# 2.0:

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

В случае не List<T> (ваше редактирование вопроса) вы можете обернуть это что-то вроде ниже (untested):

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, это должно работать. И довольно удобно, с С# 3.0 вы можете добавить this до IList/IList<T> и использовать его как метод расширения. Единственная тонкость заключается в том, что параметр anon-method будет object, поэтому вам нужно отбросить его.

Ответ 2

Вы можете просто заменить исходный список на отфильтрованный:

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

Ответ 3

Создайте новый список 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;
}

Ответ 4

Также вы можете просто использовать цикл while.

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