web-dev-qa-db-fra.com

La collection a été modifiée. opération d'énumération peut ne pas s'exécuter dans ArrayList

J'essaie de supprimer un élément d'un ArrayList et j'obtiens cette exception:
Collection was modified; enumeration operation may not execute.

Des idées?

76
Ricardo

Vous supprimez l'élément pendant un foreach, oui? Simplement, vous ne pouvez pas. Il y a quelques options communes ici:

  • utilisation List<T> et RemoveAll avec un prédicat
  • itérer en arrière par index, en supprimant les éléments correspondants

    for(int i = list.Count - 1; i >= 0; i--) {
        if({some test}) list.RemoveAt(i);
    }
    
  • utilisez foreach et placez les éléments correspondants dans une seconde liste; maintenant énumérer la deuxième liste et supprimer ces éléments de la première (si vous voyez ce que je veux dire)

189
Marc Gravell

Voici un exemple (désolé pour les fautes de frappe)

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

OU si vous utilisez la version 3.5, Linq facilite le premier bit:

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

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

Remplacez "..." par votre condition qui détermine si l'élément doit être supprimé.

23
Will

Une solution consiste à ajouter le ou les éléments à supprimer à une nouvelle liste. Puis parcourez et supprimez ces éléments.

8
ozczecho

J'aime itérer en arrière en utilisant une boucle for, mais cela peut être fastidieux comparé à foreach. Une solution que j’aime est de créer un énumérateur qui parcourt la liste à l’arrière. Vous pouvez implémenter cela en tant que méthode d’extension sur ArrayList ou List<T>. L'implémentation de ArrayList est ci-dessous.

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

L'implémentation pour List<T> est similaire.

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

L'exemple ci-dessous utilise l'énumérateur pour supprimer tous les entiers pairs d'un 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);
    }
8
Joe B

Ne modifiez pas la liste dans une boucle qui parcourt la liste.

À la place, utilisez for() ou while() avec un index, en revenant en arrière dans la liste. (Cela vous permettra de supprimer des choses sans obtenir un index invalide.)

var foo = new List<Bar>();

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

Est-ce que je manque quelque chose? Quelqu'un me corrige si je me trompe.

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

Au lieu de foreach (), utilisez une boucle for () avec un index numérique.

2
Seva Alekseyev

Je suis d'accord avec plusieurs des points que j'ai lus dans ce billet et que je les ai intégrés dans ma solution pour résoudre exactement le même problème que celui de la publication d'origine.

Cela dit, les commentaires que j'ai appréciés sont les suivants:

  • "sauf si vous utilisez .NET 1.0 ou 1.1, utilisez List<T> au lieu de ArrayList. "

  • "Ajoutez également les éléments à supprimer à une nouvelle liste. Parcourez-les et supprimez-les." .. dans mon cas, je viens de créer une nouvelle liste et de la renseigner avec les valeurs de données valides.

par exemple.

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's 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;
}        
2
Brian Wells

en utilisant ArrayList vous pouvez aussi essayer comme ça

ArrayList arraylist = ... // myobject data list

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

foreach (var item in temp)
{
      if (...)
         arraylist.Remove(item);
}
1
Nikson Kanti Paul