2010-08-17 16 views
21
foreach(BruteforceEntry be in Entries.Values) 
{ 
    if (be.AddedTimeRemove <= now) 
     Entries.Remove(be.IPAddress); 
    else if (be.Unbantime <= now && be.Unbantime.Day == DateTime.Now.Day) 
     Entries.Remove(be.IPAddress); 
} 

È stata generata un'eccezione:Modificare un dizionario che sto scorrendo

Collection è stato modificato; l'operazione di enumerazione potrebbe non essere eseguita.

Per qualche motivo, non lo è più.

So che non è possibile rimuovere qualcosa, mentre iterando attraverso di essa in questo modo. La mia domanda è: come risolverlo?

risposta

34

Non è possibile modificare una raccolta su cui si sta iterando. In questo caso, una soluzione molto migliore sarebbe quella di creare un elenco di voci da rimuovere con l'iterazione di dizionario, e poi iterare che lista, rimuovere le voci dal dizionario:

List<string> removals = new List<string>();      
DateTime now = DateTime.Now; 
foreach(BruteforceEntry be in Entries.Values) 
{ 
    if (be.AddedTimeRemove <= now || 
     (be.Unbantime <= now && be.Unbantime.Day == DateTime.Now.Day)) 
    { 
     removals.Add(be.IPAddress); 
    } 
} 
foreach (string address in removals) 
{ 
    Entries.Remove(address); 
} 

Nota che se si sta utilizzando .NET 3.5, è possibile utilizzare una query LINQ per esprimere la prima parte:

List<string> removals = (from be in Entries.Values 
         where be.AddedTimeRemove <= now || 
           (be.Unbantime <= now && 
           be.Unbantime.Day == DateTime.Now.Day) 
         select be.IPAddress).ToList(); 
+1

Grazie mille. Ho 1 domanda, ti piace questo più che semplicemente iterando attraverso una copia, usando .ToList() poiché si tratta di una modifica più piccola. – Basser

+1

@Basser: Beh, per prima cosa è probabile che abbia un ingombro di memoria inferiore. Sta anche solo iterando sull'intera collezione una volta: la tua soluzione corrente esegue una volta una volta l'iterazione della lista, quindi itera su quella lista. Non fraintendermi - funzionerà ... Preferisco questo approccio. –

+0

beh, mi hanno appena detto che un utente SO top ha risposto alla mia domanda, ecco perché pensavo di chiederti! Immagino che userò questo metodo, dato che sei corretto! Grazie ancora. Non sono ancora molto bravo con le raccolte, sto ancora imparando. – Basser

1

Non è possibile modificare la raccolta durante l'iterazione con foreach. Invece, scorrere su di esso con un ciclo for.

+2

Se la vostra intenzione di essere la rimozione di elementi della collezione, assicurati di scorrere su di esso in reverse (da length-1 a 0) e non 0 a length-1 –

+1

È meglio iterare all'indietro se si rimuovono gli articoli e si aggiorna la variabile di incremento di conseguenza se si aggiunge. Questo è soggetto a errori. – Marc

+5

L'iterazione su un * dizionario * con un ciclo for è comunque abbastanza complicato ... –

2

È possibile modificare foreach(BruteforceEntry be in Entries.Values)-foreach(BruteforceEntry be in new List<BruteforceEntry>(Entries.Values))

In questo modo non si modifica la vostra collezione, ma ra ne ne copia una copia.

+0

Grazie, questo funzionerebbe se qualcun altro non rispondesse. – Basser

+0

Ciò non funzionerà perché si sta ancora ottenendo un iteratore dall'elenco <>. È l'iteratore, non la raccolta, che sta causando il problema. –

+0

Funziona sicuramente, non si sta rimuovendo dall'elenco iteratore <>, si sta rimuovendo dal dizionario <> iteratore. L'elenco <> enumera il dizionario completo <> iteratore prima della prima rimozione dal dizionario. –

0

ebollizione il vostro problema ...

foreach(... ... in Entries.Values) 
{ 
     Entries.Remove(...); 

} 

Come gli altri hanno detto si sta modificando l'iteratore, mentre l'iterazione.

È possibile, come dice @David, utilizzare un ciclo for, ma assicurarsi di iniziare alla fine (iterazione inversa).

8

In poche parole: non è possibile rimuovere una voce da una raccolta mentre si sta iterando su di essa.

Una possibile soluzione consiste nel creare una copia della collezione (ad esempio utilizzando ToList) e iterare che:

foreach(BruteforceEntry be in Entries.Values.ToList()) 
{ 
    // modify the original collection 
} 
+0

Grazie, questo ha risolto la mia domanda. – Basser

+0

Questo non funzionerà perché si sta ancora ottenendo un iteratore dall'elenco <>. È l'iteratore, non la raccolta, che sta causando il problema. –

+0

La mia soluzione era solo un esempio di soluzione alternativa. In termini di prestazioni e chiarezza, ti suggerisco di adottare la soluzione di Jon;) – digEmAll

2

Sembra che la tua domanda riguarda il motivo per cui l'eccezione è non essere gettato in cui lo era. Come le altre risposte dichiarano, generalmente non è possibile modificare una collezione attraverso cui si sta iterando, ma si sta iterando sulla raccolta Values, che, credo, è una copia dei valori nel dizionario, piuttosto che fare riferimento alla raccolta di dizionari principale si. Quindi non ha più il problema di iterare e modificare la stessa cosa.

1

questo (a mio parere) è il modo più semplice:

Dictionary<String, String> A = new Dictionary<string, string>(); //Example Dictionary 
A.Add("A", "A"); //Example Values 
A.Add("B", "B"); 
A.Add("C", "C"); 

for (int i = A.Count - 1; i >= 0; i--) //Loop backwards so you can remove elements. 
{ 
    KeyValuePair<String, String> KeyValue = A.ElementAt(i); //Get current Element. 
    if (KeyValue.Value == "B") A.Remove(KeyValue.Key); 
} 

Nel tuo caso:

for (int i = Entries.Count - 1; i >= 0; i--) 
{ 
    KeyValuePair<String, BruteforceEntry> KeyValue = Entries.ElementAt(i); 
    if (KeyValue.Value.AddedTimeRemove <= now) 
     Entries.Remove(KeyValue.Key); 
    else if (KeyValue.Value.Unbantime <= now && KeyValue.Value.Unbantime.Day == DateTime.Now.Day) 
     Entries.Remove(KeyValue.Key); 
} 
+1

-1 Sebbene questo possa funzionare per alcune implementazioni di .NET, la classe 'Dictionary ' non ** garantisce ** che l'ordine dei suoi elementi (quando è iterato come 'IEnumerable > ') è invariato quando il dizionario viene modificato. – Ergwun

+1

Da http://msdn.microsoft.com/en-us/library/xfhwa508.aspx: "Ai fini dell'enumerazione, ogni elemento nel dizionario viene considerato come una struttura KeyValuePair che rappresenta un valore e la sua chiave. L'ordine in cui vengono restituiti gli articoli non è definito." – Ergwun

+1

Ah, non lo sapevo, grazie per il chiarimento! – Blam

Problemi correlati