2010-05-07 17 views
13

Tengo dos colecciones de cadenas: CollectionA es una propiedad StringCollection de un objeto almacenado en el sistema, mientras que CollectionB es una lista generada en tiempo de ejecución. CollectionA necesita actualizarse para que coincida con CollectionB si hay alguna diferencia. Así que ideé lo que esperaba que fuera un método LINQ simple para realizar la eliminación.¿Por qué me aparece "La colección se modificó, la operación de enumeración no se puede ejecutar" cuando no se modifica la colección enumerada?

var strDifferences = CollectionA.Where(foo => !CollectionB.Contains(foo)); 
foreach (var strVar in strDifferences) { CollectionA.Remove(strVar); } 

pero estoy recibiendo un error en "Collection was modified; enumeration operation may not execute" strDifferences ... a pesar de que es enumerable una separada de la colección está modificando! Originalmente ideé este explícitamente para evadir este error, ya que mi primera implementación lo produciría (como estaba enumerando en CollectionA y acaba de eliminar cuando !CollectionB.Contains(str)). ¿Alguien puede arrojar alguna idea de por qué esta enumeración está fallando?

Respuesta

21

Los dos no están completamente separados. Where no hace una copia separada de la colección. Guarda internamente una referencia a la colección original y obtiene elementos a medida que los solicita.

Puede resolver su problema agregando ToList() para forzar Where para recorrer la colección de forma inmediata.

var strDifferences = CollectionA 
    .Where(foo => !CollectionB.Contains(foo)) 
    .ToList(); 
+0

¿No 'ToArray() 'sería mejor aquí? No hay necesidad de usar 'List'. – svick

+0

@svick Eso es algo que me he preguntado durante bastante tiempo, pero esa sería una pregunta que haría otro día. Siempre que no se haya preguntado, por supuesto -chuckle- –

+1

@GraceNote atrapa eso aquí: http://stackoverflow.com/questions/1105990/is-it-better-to-call-tolist-or-toarray-in -linq-queries – nawfal

3

Where, transferirá IEnumerable<T> y luego se esté utilizando y que en su foreach (var strVar in strDifferences)

Luego se le tratando de sacarlo de la colección que creó el IEnumerable<T>. No ha creado una nueva lista, hace referencia al CollectionA para extraer el siguiente elemento, por lo que no puede editar CollectionA.

Usted puede hacer esto también:

var strDifferences = CollectionA.Where 
    (foo => CollectionB.Contains(foo)).ToList(); 

CollectionA = strDifferences; 
//or instead of reassigning CollectionA 
CollectionA.Clear(); 
CollectionA.AddRange(strDifferences); 

Dado que va a quitar los que no están en CollectionB. Solo busque los que sí lo son, cree una lista y asigne esa lista a la variable CollectionA.

+0

Ah ... Me encantaría usar su alternativa (sin embargo, tiene un efecto idéntico), pero desafortunadamente CollectionA, es una propiedad de solo lectura, así que no puedo configurarla. Aunque das una explicación mucho más profunda, entonces +1 para eso. –

+0

@ccornet si no puede configurarlo, puede borrarlo y agregar los elementos nuevamente. – kemiller2002

+0

Lo pensé largo y tendido. Decidí seguir con el enfoque de eliminación. No deseo modificar CollectionA si no es necesario realizar ningún cambio, crear una lista de lo que debe eliminarse hace que esto sea muy rápido y simple de saber (solo vea si strDifferences.Count> 0). Si construyo una matriz de la meta, igual tendría que verificar si CollectionA tiene algo que necesita ser eliminado. –

3

intente modificar la primera línea un poco:

var strDifferences = 
    CollectionA.Where(foo => !CollectionB.Contains(foo)).ToList(); 

creo que está utilizando LINQ ejecución perezoso de la consulta. La llamada al ToList forzará la ejecución de la consulta antes de enumerar.

+0

Sí, y asegúrese de no ponerlo sobre dos líneas yo..e. var strDifferencesTemp = CollectionA.Where (foo =>! CollectionB.Contains (foo)); var strDifferences = strDifferencesTemp.ToList(); si lo hace, la colección puede ser modificada por otro hilo entre estas dos líneas. – Markus

Cuestiones relacionadas