Pregunta

    

Esta pregunta ya tiene una respuesta aquí:

         

Duplicar

Modificación de una colección mientras se itera a través de ella


¿Alguien tiene un patrón agradable que me permita evitar la imposibilidad de eliminar objetos mientras recorro una colección enumerable (por ejemplo, un IList o KeyValuePairs en un diccionario)

Por ejemplo, lo siguiente falla, ya que modifica la Lista que se enumera durante el foreach

foreach (MyObject myObject in MyListOfMyObjects)
{
     if (condition) MyListOfMyObjects.Remove(myObject);
}

En el pasado he usado dos métodos.

He reemplazado el foreach con un bucle for invertido (para no cambiar los índices que estoy recorriendo si elimino un objeto).

También he intentado almacenar una nueva colección de objetos para eliminar dentro del ciclo, luego recorrer esa colección y eliminar los objetos de la colección original.

Funcionan bien, pero ninguno se siente bien, y me preguntaba si alguien ha encontrado una solución más elegante para el problema

¿Fue útil?

Solución

Hay un método List<T>.RemoveAll(Predicate<T> match) útil que creo que está diseñado para esto: http : //msdn.microsoft.com/en-us/library/wdka673a.aspx

Otros consejos

Es un poco simple, pero cuando planeo eliminar elementos de un IEnumerable / IList, generalmente solo hago una copia:

foreach (MyObject myObject in new List<MyObject>(MyListOfMyObjects))
{
     if (condition) MyListOfMyObjects.Remove(myObject);
}

No es la forma más eficiente de hacerlo, pero es fácil de leer. Optimización prematura y todo eso.

Haga lo inverso, creando una nueva lista:

List myFilteredList = new List();
foreach (MyObject myObject in myListOfMyObjects)
{
     if (!condition) myFilteredList.Add(myObject);
}

Luego use la nueva lista donde la necesite.

También puede usar una expresión LINQ fácilmente, nuevamente, invirtiendo la condición. Esto tiene el beneficio adicional de no crear una nueva estructura, pero también las desventajas de que sea un enumerable vago:

var myFilteredList = from myObject in myListOfMyObjects
                     where !condition
                     select myObject;

Sin embargo, si realmente necesita eliminar los elementos de la lista, normalmente uso " crear una nueva lista, luego reiterar y eliminar " enfoque.

Acabo de encontrar esta publicación y pensé en compartirla.

void RemoveAll(object condition)  
{

    bool found = false;

    foreach(object thisObject in objects)    
    {

        if (condition)    
        {    
            objects.Remove(thisObject);

            found = true;

            break; //exit loop    
        }     
     }

    // Call again recursively

    if (found) RemoveAll(condition);

}

No me gusta la idea del bucle invertido, ya que solo funciona en ciertas estructuras de datos.

En general, usaría la segunda técnica y acumularía los elementos que se eliminarán en una colección separada para "eliminar". Si la eliminación puede invalidar las iteraciones existentes (como ocurrirá con cualquier colección de árbol equilibrada, por ejemplo), entonces no veo una forma de evitar esto.

La única otra técnica que he usado ocasionalmente es reiniciar toda la iteración cuando encuentre el primer elemento para eliminar. Si lo hace sin encontrar ningún elemento para eliminar, la función habrá finalizado. Esto es ineficiente, pero a veces es necesario si eliminar un elemento de la colección puede cambiar el conjunto de elementos que deben eliminarse.

Tengo un diccionario y quiero deshacerme de todos los valores. Cuando se elimina cada valor, se elimina del diccionario, lo que crea el problema que discute. Hice lo siguiente:

foreach (var o in dictionary.Values.ToList())
{
  o.Dispose();
}

Aprecio que esto pueda estar muerto ahora, pero la forma en que siempre hago esto es:

foreach (MyObject myObject en MyListOfMyObjects)
{

if (condición) MyListOfMyObjects.Remove (myObject);

break;

}

¡El objeto se elimina y luego sale el bucle, viola!

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top