2010-05-07 34 views
13

Ho due raccolte di stringhe: CollectionA è una proprietà StringCollection di un oggetto memorizzato nel sistema, mentre CollectionB è una lista generata in fase di esecuzione. La raccolta deve essere aggiornata per corrispondere alla raccolta B se vi sono differenze. Quindi ho ideato quello che mi aspettavo fosse un semplice metodo LINQ per eseguire la rimozione.Perché sto ricevendo "La raccolta è stata modificata, l'operazione di enumerazione potrebbe non essere eseguita" quando non si modifica la raccolta enumerata?

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

Ma sto ottenendo un errore di "Collection was modified; enumeration operation may not execute" su strDifferences ... anche se è enumerabile un separato dalla collezione in corso di modifica! Inizialmente ho ideato questo in modo esplicito per eludere questo errore, in quanto la mia prima implementazione lo avrebbe prodotto (mentre stavo enumerando su CollectionA e rimuovendolo solo quando !CollectionB.Contains(str)). Qualcuno può far luce sul perché questa enumerazione sta fallendo?

risposta

21

I due non sono completamente separati. Where non crea una copia separata della raccolta. Mantiene internamente un riferimento alla collezione originale e recupera elementi da esso come richiesto.

È possibile risolvere il problema aggiungendo ToList() per forzare Where per iterare immediatamente attraverso la raccolta.

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

Non sarebbe 'ToArray()' essere meglio qui? Non è necessario usare 'List'. – svick

+0

@svick Questo è qualcosa che mi sono chiesto per un po 'di tempo, ma sarebbe una domanda che vorrei chiedere in un altro giorno. A patto che non sia già stato chiesto, ovviamente -chuckle- –

+1

@GraceNote, prendi questo qui: http://stackoverflow.com/questions/1105990/is-it-better-to-call-tolist-or-toarray-in -linq-query – nawfal

3

Where passa indietro IEnumerable<T> e poi si utilizza che nel vostro foreach (var strVar in strDifferences)

Si sta poi cercando di rimuoverlo dalla collezione che ha creato il IEnumerable<T>. Non hai creato un nuovo elenco, fa riferimento a CollectionA per estrarre l'elemento successivo da, quindi non puoi modificare CollectionA.

È possibile farlo anche:

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

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

Dal momento che si sta rimuovendo quelli che non sono in CollectionB. Cerca solo quelli che sono, crea una lista e assegna quella lista alla variabile CollectionA.

+0

Ah ... Mi piacerebbe usare la tua alternativa (comunque identica in effetti lo è), ma purtroppo CollectionA, si scopre, è una proprietà di sola lettura, quindi non posso impostarla. Tu dai una spiegazione molto più approfondita, però, quindi +1 per quello. –

+0

@ccornet se non è possibile impostarlo, è possibile cancellarlo e aggiungere nuovamente gli elementi. – kemiller2002

+0

Ci ho pensato a lungo e duramente. Ho deciso di andare ancora con l'approccio di rimozione. Non desidero modificare CollectionA se non è necessario apportare modifiche, la creazione di un elenco di ciò che deve essere rimosso rende questo molto semplice e rapido da sapere (basta vedere se strDifferences.Count> 0). Se costruisco un array dell'obiettivo, dovrei comunque verificare se CollectionA ha qualcosa che deve essere rimosso. –

3

provare a modificare la prima linea un po ':

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

Penso LINQ sta usando esecuzione pigro della query. La chiamata a ToList imporrà l'esecuzione della query prima dell'enumerazione.

+0

Sì, e assicurati di non metterlo su due linee io..e. var strDifferencesTemp = CollectionA.Where (foo =>! CollectionB.Contains (foo)); var strDifferences = strDifferencesTemp.ToList(); se lo si fa, la raccolta può essere modificata da qualche altro thread tra queste due linee. – Markus

Problemi correlati