2010-01-07 12 views
62

Estoy intentando quitar un elemento de un ArrayList y yo conseguir este Excepción:
Collection was modified; enumeration operation may not execute.La colección se modificó; la operación de enumeración no podrá ejecutar en ArrayList

¿Alguna idea?

+11

Para el registro, a menos que esté utilizando .NET 1.0 o 1.1, es probable que debe utilizar '' Lista en lugar de 'ArrayList'. –

+2

¿Por qué este duplicado obtengo este error ** a veces ** en 'Application.Exit();' y no manipulo ninguna colección ni aplicación. – Bitterblue

+0

foreach (artículo ItemCollection en ItemCollection.Values) Para foreach (ItemCollection s en ItemCollection.Values.ToList()) La cuestión es que ItemCollection.Values ​​se está modificando en el interior del bucle foreach. Llamar a ItemCollection.Values.ToList() copia los valores de los suscriptores. Valores en una lista separada al inicio de foreach –

Respuesta

167

que está eliminando el elemento durante un foreach, sí? Simplemente, no puedes. Hay algunas opciones comunes aquí:

  • uso List<T> y RemoveAll con un predicado
  • iterate hacia atrás por el índice, la eliminación de elementos que coincidan con

    for(int i = list.Count - 1; i >= 0; i--) { 
        if({some test}) list.RemoveAt(i); 
    } 
    
  • uso foreach, y poner elementos que coinciden en una segunda lista; Ahora enumerar la segunda lista y eliminar los elementos de la primera (si se entiende lo que quiero decir)

+0

+1, demasiado rápido para mí :) – Glenn

+0

Veo lo que quiere decir ... intentaré hacerlo – Ricardo

+4

si la colección no es tan grande, un simple .ToArray() y enumerando que en su lugar podría ser lo suficientemente bueno también. –

5

No modifique la lista dentro de un bucle que itera por la lista.

En su lugar, utilice un for() o while() con un índice, retrocediendo en la lista. (Esto le permitirá eliminar las cosas sin conseguir un índice no válido.)

var foo = new List<Bar>(); 

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

¡Ajustar! Entonces, ¿cómo debería hacerlo? – Ricardo

+1

Use un bucle regular en su lugar (durante/while). El problema es usar una instrucción 'foreach' (es decir, un enumerador). –

+0

@silky f está utilizando un for() o while() para recorrer la colección, asegúrese de hacerlo desde la parte posterior/superior, comenzando en collection.count-1 y bajando a 0. De lo contrario, lo hará golpear el mismo problema –

2

En lugar de foreach(), utilice un bucle for() con un índice numérico.

+0

* Debes * contar hacia atrás si usas este enfoque. –

+2

@Rob - no * debe *, pero es más complicado si avanza; más formas de equivocarse y terminar de una en una. –

+0

Un ejemplo de código específico es más deseado que solo una explicación, hablando como alguien que busca una solución a esto. – theJerm

8

Una forma es agregar los elementos que se eliminarán a una nueva lista. Luego revise y elimine esos elementos.

+0

esta es la forma en que lo abordé y es mucho más claro leer el código de esta manera. – Chris

19

He aquí un ejemplo (lo siento por ninguna errata)

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

O si está utilizando 3.5, LINQ hace que el primer bit más fácil:

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

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

reemplazar "..." con su condición que determina si el artículo debe ser eliminado.

+1

Si está utilizando "Lista genérica" ​​(que interpreto como 'Lista '), simplemente use 'list.RemoveAll (item => ...)'. –

7

Me gusta iterar hacia atrás usando un loop for, pero esto puede ser tedioso en comparación con foreach. Una solución que me gusta es crear un enumerador que atraviese la lista hacia atrás. Puede implementar esto como un método de extensión en ArrayList o List<T>. La implementación para ArrayList está debajo.

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

La implementación para List<T> es similar.

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

El ejemplo siguiente utiliza el empadronador para eliminar todos los enteros pares de 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); 
    } 
2

Estoy de acuerdo con varios de los puntos que he leído en este post y los he incorporado en mi solución para resolver el problema exactamente el mismo que el envío original.

Dicho esto, los comentarios he apreciado son: "a menos que esté utilizando .NET 1.0 o 1.1, utilice List<T> en lugar de ArrayList"

  • "Además, agrega el artículo (s) para ser eliminados en una nueva lista. Luego revise y elimine esos elementos ". .. en mi caso acabo de crear una nueva lista y la llené con los valores de datos válidos.

e.g.

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

¿Falta algo? Alguien me corrige si estoy equivocado.

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

usando ArrayList también se puede tratar como esto

ArrayList arraylist = ... // myobject data list 

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

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