Коллекция была изменена; операция перечисления может не выполняться в ArrayList

Я пытаюсь удалить элемент из ArrayList, и я получаю это исключение:
Collection was modified; enumeration operation may not execute.

Любые идеи?

Ответ 1

Вы удаляете элемент во время foreach, да? Просто вы не можете. Здесь есть несколько общих вариантов:

  • используйте List<T> и RemoveAll с предикатом
  • итерация назад по индексу, удаление совпадающих элементов

    for(int i = list.Count - 1; i >= 0; i--) {
        if({some test}) list.RemoveAt(i);
    }
    
  • используйте foreach и поместите совпадающие элементы во второй список; теперь перечислите второй список и удалите эти элементы из первого (если вы понимаете, что я имею в виду)

Ответ 2

Вот пример (извините за любые опечатки)

var itemsToRemove = new ArrayList();  // should use generic List if you can

foreach (var item in originalArrayList) {
  if (...) {
    itemsToRemove.Add(item);
  }
}

foreach (var item in itemsToRemove) {
  originalArrayList.Remove(item);
}

ИЛИ, если вы используете 3.5, Linq делает первый бит проще:

itemsToRemove = originalArrayList
  .Where(item => ...)
  .ToArray();

foreach (var item in itemsToRemove) {
  originalArrayList.Remove(item);
}

Замените "..." своим условием, которое определяет, должен ли элемент быть удален.

Ответ 3

Один из способов - добавить элементы (элементы), которые нужно удалить, в новый список. Затем пройдите и удалите эти элементы.

Ответ 4

Мне нравится итератировать назад, используя цикл for, но это может стать утомительным по сравнению с foreach. Одно из решений, которое мне нравится, - создать перечислитель, который перемещает список назад. Вы можете реализовать это как метод расширения на ArrayList или List<T>. Реализация для ArrayList ниже.

    public static IEnumerable GetRemoveSafeEnumerator(this ArrayList list)
    {
        for (int i = list.Count - 1; i >= 0; i--)
        {
            // Reset the value of i if it is invalid.
            // This occurs when more than one item
            // is removed from the list during the enumeration.
            if (i >= list.Count)
            {
                if (list.Count == 0)
                    yield break;

                i = list.Count - 1;
            }

            yield return list[i];
        }
    }

Реализация для List<T> аналогична.

    public static IEnumerable<T> GetRemoveSafeEnumerator<T>(this List<T> list)
    {
        for (int i = list.Count - 1; i >= 0; i--)
        {
            // Reset the value of i if it is invalid.
            // This occurs when more than one item
            // is removed from the list during the enumeration.
            if (i >= list.Count)
            {
                if (list.Count == 0)
                    yield break;

                i = list.Count - 1;
            }

            yield return list[i];
        }
    }

В приведенном ниже примере используется перечислитель для удаления всех четных целых чисел из ArrayList.

    ArrayList list = new ArrayList() {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    foreach (int item in list.GetRemoveSafeEnumerator())
    {
        if (item % 2 == 0)
            list.Remove(item);
    }

Ответ 5

Не изменяйте список внутри цикла, который выполняет итерацию по списку.

Вместо этого используйте for() или while() с индексом, идущим назад по списку. (Это позволит вам удалять вещи без получения недопустимого индекса.)

var foo = new List<Bar>();

for(int i = foo.Count-1; i >= 0; --i)
{
  var item = foo[i];
  // do something with item
}

Ответ 6

Я что-то упустил? Кто-то поправьте меня, если я ошибаюсь.

list.RemoveAll(s => s.Name == "Fred");

Ответ 7

Вместо foreach() используйте цикл for() с числовым индексом.

Ответ 8

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

Тем не менее, комментарии, которые я оценил, следующие:

  • ", если вы не используете .NET 1.0 или 1.1, используйте List<T> вместо ArrayList."

  • "Кроме того, добавьте элементы, которые нужно удалить, в новый список. Затем выполните и удалите эти элементы." .. в моем случае я только что создал новый список и заполнил его действительными значениями данных.

например.

private List<string> managedLocationIDList = new List<string>();
string managedLocationIDs = ";1321;1235;;" // user input, should be semicolon seperated list of values

managedLocationIDList.AddRange(managedLocationIDs.Split(new char[] { ';' }));
List<string> checkLocationIDs = new List<string>();

// Remove any duplicate ID and cleanup the string holding the list if ID's
Functions helper = new Functions();
checkLocationIDs = helper.ParseList(managedLocationIDList);

...
public List<string> ParseList(List<string> checkList)
{
    List<string> verifiedList = new List<string>();

    foreach (string listItem in checkList)
    if (!verifiedList.Contains(listItem.Trim()) && listItem != string.Empty)
        verifiedList.Add(listItem.Trim());

    verifiedList.Sort();
    return verifiedList;
}        

Ответ 9

используя ArrayList, вы также можете попробовать это

ArrayList arraylist = ... // myobject data list

ArrayList temp = (ArrayList)arraylist.Clone();

foreach (var item in temp)
{
      if (...)
         arraylist.Remove(item);
}