2011-01-17 20 views
12

Muy a menudo necesito algo así:Práctica recomendada para evitar InvalidOperationException: ¿Se modificó la recopilación?

foreach (Line line in lines) 
{ 
    if (line.FullfilsCertainConditions()) 
    { 
     lines.Remove(line) 
    } 
} 

Esto no funciona, porque siempre me dan una InvalidOperationException porque el enumerador se cambió durante el bucle.

así que cambié todos mis bucles de este tipo a lo siguiente:

List<Line> remove = new List<Line>(); 
foreach (Line line in lines) 
{ 
    if (line.FullfilsCertainConditions()) 
    { 
     remove.Add(line) 
    } 
} 

foreach (Line line in remove) { 
{ 
    lines.Remove(line); 
} 

No estoy seguro si esto es realmente la mejor manera ya que en el peor de los casos tengo que repetir 2 veces sobre el original lista y necesita tiempo 2n en lugar de n.

¿Hay una mejor manera de hacerlo?

EDIT:!

yo era capaz de hacer que el uso de respuesta de Mark Pero ¿y si no lo hace mi colección de aperos RemoveAll()?

Por ejemplo, un EDITAR

System.Windows.Controls.UIElementCollection

2:

vez más con la ayuda de Marcos ahora soy capaz de hacer la siguiente llamada a eliminar todos ScatterViewItems:

CollectionUtils.RemoveAll(manager.getWindow().IconDisplay.Items, elem => elem.GetType() == typeof(ScatterViewItem)); 
+0

tengo el mismo problema hace algún tiempo y no hay solución. Es incluso peor: no es 2n sino n^2 ya que 'lines.Remove (line)' itera nuevamente sobre la colección. – Matten

+0

Pero O (2n) es igual a O (n) :-) realmente: en Java puede usar un iterador para hacer esto, o ir a una implementación de colección de copia en escritura que permite modificaciones mientras itera. – Waldheinz

+0

Re la actualización - ver mi edición –

Respuesta

17

Esto es directo horneado Ly en List<T>:

lines.RemoveAll(line => line.FullfilsCertainConditions()); 

o en C# 2.0:

lines.RemoveAll(delegate(Line line) { 
    return line.FullfilsCertainConditions(); 
}); 

En el caso de no List<T> (tu edición en la pregunta), que podrían terminar con esto algo parecido a continuación (no probado) :

static class CollectionUtils 
{ 
    public static void RemoveAll<T>(IList<T> list, Predicate<T> predicate) 
    { 
     int count = list.Count; 
     while (count-- > 0) 
     { 
      if (predicate(list[count])) list.RemoveAt(count); 
     } 
    } 
    public static void RemoveAll(IList list, Predicate<object> predicate) 
    { 
     int count = list.Count; 
     while (count-- > 0) 
     { 
      if (predicate(list[count])) list.RemoveAt(count); 
     } 
    } 
} 

Desde UIElementCollection implementa el (no genérico) IList esto debería funcionar. Y bastante cómodamente, con C# 3.0 puede agregar un this antes de IList/IList<T> y tenerlo como un método de extensión. La única sutileza es que el parámetro para el método anon será object, por lo que deberá descartarlo.

+0

¿Hay alguna manera de lograr lo mismo en .NET 2.0? – Matten

+2

@Matten: agregó un ejemplo de C# 2.0. –

+0

@Marc Gravell - muy simple, muy elegante. Gracias :) – Matten

1

Usted puede simplemente reemplazar la lista original con una filtrada:

lines = lines.Where(line => line.FullfilsCertainConditions()).ToList(); 
1

construir una nueva lista instaed:

public IList<Line> GetListWithoutFullfilsCertainConditions(IList<Line> fullList) 
{ 
    IList<Line> resultList = new List<Line>(fullList.Count); 

    foreach (Line line in fullList) 
    { 
     if (!line.FullfilsCertainConditions()) 
     { 
      resultList.Add(line) 
     } 
    } 

    return resultList; 
} 
+0

Motivo del voto a favor, por favor? –

1

También puede simplemente utilizar el bucle while.

int i = 0; 
while(i < lines.Count) 
{ 
    if (lines[i].FullfilsCertainConditions()) 
    { 
    lines.RemoveAt(i); 
    } 
    else {i++;} 
} 
Cuestiones relacionadas