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?
Vous supprimez l'élément pendant un foreach
, oui? Simplement, vous ne pouvez pas. Il y a quelques options communes ici:
List<T>
et RemoveAll
avec un prédicatité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)
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é.
Une solution consiste à ajouter le ou les éléments à supprimer à une nouvelle liste. Puis parcourez et supprimez ces éléments.
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);
}
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
}
Est-ce que je manque quelque chose? Quelqu'un me corrige si je me trompe.
list.RemoveAll(s => s.Name == "Fred");
Au lieu de foreach (), utilisez une boucle for () avec un index numérique.
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;
}
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);
}