2011-02-15 46 views
7

FaC# prevenire Collection è stata modificata eccezione

foreach(T value in new List<T>(oldList)) 

è pericoloso (costosa) quando oldList contiene 1 milioni di oggetto T?

Più Generaly qual è il modo migliore per enumerare oltre oldList dato che gli elementi possono essere aggiunti/rimossi durante l'enumerazione ...

+0

Sembra che ci sono due domande differenti qui. Non sono sicuro del motivo per cui sono stati combinati. –

risposta

4

Io di solito basta a creare un elenco di tutti gli oggetti da rimuovere o aggiunti.

All'interno del foreach ho solo aggiungere gli elementi alle collezioni appropriate e modificare la raccolta originale dopo la foreach hanno completato (anello attraverso la raccolta removeItems e addItems)

7

La regola generale è, non si dovrebbe modificare lo stesso collezione in cui si sta enumerando. Se vuoi fare qualcosa del genere, mantieni un'altra raccolta che tenga traccia degli elementi da aggiungere/rimuovere dalla collezione originale e poi, dopo essere usciti dal ciclo, esegui l'operazione di aggiunta/rimozione nella raccolta originale.

+0

Ok ma in realtà è un altro thread che può modificare la collezione. Per qualche motivo, non riesco a sincronizzare l'iterazione con l'aggiunta/rimozione. – Toto

+0

Come accennato nella mia risposta aggiornata, se si utilizza .NET 4.0, è consigliabile utilizzare le raccolte simultanee. –

0

Sarà "lento" ma non c'è molto altro da fare su di esso, se non eseguendolo su un thread in background. Per esempio. utilizzando un BackgroundWorker.

Se le operazioni nell'elenco si verificano solo su un thread, l'approccio corretto consiste nell'aggiungere gli elementi da aggiungere/rimuovere in elenchi separati ed eseguire quelle operazioni una volta terminate le iterazioni.

Se si utilizzano più thread, è necessario esaminare la programmazione con multithreading, ad es. utilizzare locks o probabilmente meglio un ReaderWriterLock.

AGGIORNAMENTO: Come indicato in un altro Stack Overflow question, questo è ora possibile senza alcuno sforzo in .NET 4.0 when using concurrent collections.

0

Si potrebbe scorrere l'elenco senza l'utilizzo di un enumeratore, in modo da fare qualcosa di simile ...

for(int i = 0;i<oldList.Count;i++) { 
    var value = oldList[i]; 

    ... 

    if(itemRemoveCondition) { 
    oldList.RemoveAt(i--); 
    } 
} 
+1

Trovo questo tipo di confusione; perché non semplicemente scorrere all'indietro sulla lista? Quindi non è necessario combinare l'incremento e il decremento dell'indice. –

+0

I loop all'indietro si verificano problemi con l'aggiunta di elementi. –

+0

La condizione finale per il ciclo viene valutata ad ogni iteratina o solo una volta? – Toto

1

Se vuoi dire è possibile aggiungere/rimuovere gli oggetti da un altro thread, vorrei: 1-sincronizzare il thread 2- nel Aggiungi/Rimuovi thread, creare un elenco di elementi da aggiungere o eliminare 3- e quindi eliminare questi elementi in una sezione critica (quindi è di piccole dimensioni - non è necessario sincronizzare durante l'aggiunta degli elementi alla lista di cancellazione)

Se non si desidera farlo, è possibile utilizzare per invece di raggiungere, che eviterebbe l'eccezione, ma si dovrà fare particolare attenzione in modo da non avere altri tipi di eccezioni

+0

Per qualche motivo, non riesco a sincronizzare l'iterazione con l'aggiunta/rimozione. L'iterazione è sicuramente la soluzione al mio problema. – Toto

+0

Sono felice di aiutare. Se fosse la soluzione al tuo problema, potresti contrassegnarlo come risposta, cliccando sul "segno di spunta verde"? :) Grazie! – JSBach

4

proprio come questo

var itemsToBeRemoved = new List<T>(); 

foreach (T item in myHugeList) 
{ 
    if(/*<condition>*/) 
     itemsToBeRemoved.Add(item); 
} 

myHugeList.RemoveRange(itemsToBeRemoved); 
0

Per me, la prima cosa è che si dovrebbe considerare l'utilizzo di un certo tipo di paging dei dati, perché avere una lista di articoli da 1 milione di dollari potrebbe essere pericolosa.

Avete sentito parlare del modello dell'unità di lavoro?

È possibile implementarlo in modo da contrassegnare gli oggetti per creare, aggiornare o eliminare, e in seguito, si chiama "SalvaChanges", "Commit" o qualsiasi altro facendo il lavoro di "applicare le modifiche", e avrai finito.

Ad esempio, si itera su enumerable (oldList) e li si contrassegna come "delete". In seguito, chiami "SalvaChanges" e l'unità di lavoro più generica e astratta scorrerà sul piccolo elenco filtrato di oggetti con cui lavorare.

In ogni caso, evitare le liste di un milion elementi. Dovresti lavorare con elenchi di oggetti paginati.

-2

provare per Dizionario.

int [] temp = Dictionary.Keys.ToArray();

  foreach (var c in temp) 
      { 
       if (condition) 
       { 
       //Your codes 
       } 

       else 
       { 

        Dictionary.Remove(c); 
        //OR 
        Dictionary.Add(c); 
       } 
0

foreach (valore T in new List (oldList) ToList()) - dare una prova

1

Se si utilizza Foreach per modificare la raccolta allora si otterrà questo errore come di seguito.

List<string> li = new List<string>(); 
    li.Add("bhanu"); 
    li.Add("test"); 

    foreach (string s in li) 
    { 
     li.Remove(s); 
    } 

Soluzione: utilizzare il ciclo come indicato di seguito.

for (int i = 0; i < li.Count; i++) 
    { 
     li.RemoveAt(i); 
     i--; 
    } 
0

è possibile utilizzare un flag per passare la modifica a un elenco temporaneo mentre l'originale viene enumerato.

/// dove si sta enumerazione

isBeingEnumerated = true 
foreach(T value in new List<T>(oldList)) 
isBeingEnumerated = false 
SyncList(oldList with temporaryList) 

/// dove si sta modificando durante l'enumerazione

if isBeingEnumerated then 
use a temporaryList to make the changes. 
Problemi correlati