2013-05-11 10 views
7

Sto tentando di rimuovere l'oggetto mentre sto iterando tramite Raccolta. Ma sto ottenendo un'eccezione. Come posso raggiungere questo obiettivo? Ecco il mio codice:Come aggiungere o rimuovere oggetti durante l'iterazione Raccolta in C#

foreach (var gem in gems) 
{ 
    gem.Value.Update(gameTime); 

    if (gem.Value.BoundingCircle.Intersects(Player.BoundingRectangle)) 
    { 
     gems.Remove(gem.Key); // I can't do this here, then How can I do? 
     OnGemCollected(gem.Value, Player); 
    } 
} 
+8

Prova 'foreach (var gioiello in gems.ToList())' – I4V

+0

non è possibile rimuovere un elemento, mentre l'iterazione utilizzando foreach.use un semplice per loop.foreach non è progettato per questo –

+0

qual è il messaggio di eccezione? – saeed

risposta

15

foreach è progettato per iterare su una raccolta senza modificarlo.

Per rimuovere elementi da una raccolta mentre si scorre su di essa, utilizzare un ciclo for dall'inizio alla fine.

for(int i = gems.Count - 1; i >=0 ; i--) 
{ 
    gems[i].Value.Update(gameTime); 

    if (gems[i].Value.BoundingCircle.Intersects(Player.BoundingRectangle)) 
    { 
     Gem gem = gems[i]; 
     gems.RemoveAt(i); // Assuming it's a List<Gem> 
     OnGemCollected(gem.Value, Player); 
    } 
} 

Se è una dictionary<string, Gem> per esempio, si potrebbe iterare in questo modo:

foreach(string s in gems.Keys.ToList()) 
{ 
    if(gems[s].BoundingCircle.Intersects(Player.BoundingRectangle)) 
    { 
    gems.Remove(s); 
    } 
} 
+0

Questa è una strada da percorrere! votato + –

+1

È un 'dizionario ' quindi, quindi non funzionerà. –

+0

@MatthewWatson: In tal caso, se assumiamo che la chiave sia disponibile anche nel valore come proprietà, possiamo benissimo scorrere il dizionario usando 'dictionary.Values.ToList()' e per il ciclo e quindi chiama 'dizionario .Rimuovi (item.Key); ' – Saravanan

0

Si dovrebbe usare il ciclo for invece del ciclo foreach. Si prega di fare riferimento a here

+1

No, la rimozione avrebbe rovinato l'indicizzazione. –

+2

Funzionerebbe se si tornasse indietro nell'elenco. Ma è un punto controverso perché l'OP sta usando un dizionario. –

1

Come dicono le altre risposte, un foreach è stato progettato esclusivamente per iterare su una collezione senza modificarlo come per the documenation:

la dichiarazione foreach viene utilizzato per scorrere l'insieme per ottenere le informazioni desiderate, ma non dovrebbe essere utilizzato per modificare il contenuto s della raccolta per evitare effetti collaterali imprevedibili.

al fine di fare questo è necessario utilizzare un for loop (Memorizzazione gli elementi della collezione è necessario rimuovere) e rimuoverle dalla raccolta dopo.

Tuttavia, se si utilizza un List<T> Si potrebbe fare questo:

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

Collezioni supportano foreach utilizzando Enumarator. Gli enumeratori possono essere utilizzati per leggere i dati nella raccolta, ma non possono essere utilizzati per modificare la raccolta sottostante. Se vengono apportate modifiche alla raccolta, come l'aggiunta, la modifica o l'eliminazione di elementi, l'enumeratore viene irrimediabilmente invalidato e la successiva chiamata a MoveNext o Reset genera un'eccezione InvalidOperationException. Usa per loop per la modifica della collezione.

1

Il modo più semplice è quello di fare ciò che @ IV4 suggerito:

foreach (var gem in gems.ToList()) 

Il ToList() convertirà il dizionario a un elenco di KeyValuePair, in modo da funzionare bene.

L'unica volta in cui non si vorrebbe farlo in questo modo è se si dispone di un grande dizionario da cui si rimuovono solo pochi elementi e si desidera ridurre l'uso della memoria.

Solo in questo caso sarebbe si desidera utilizzare uno dei seguenti approcci:


Fare una lista delle chiavi come li trovate, poi hanno un ciclo separato per rimuovere gli elementi:

List<KeyType> keysToRemove = new List<KeyType>(); 

foreach (var gem in gems) 
{ 
    gem.Value.Update(gameTime); 

    if (gem.Value.BoundingCircle.Intersects(Player.BoundingRectangle)) 
    { 
     OnGemCollected(gem.Value, Player); 
     keysToRemove.Add(gem.Key); 
    } 
} 

foreach (var key in keysToRemove) 
    gems.Remove(key); 

(Dove KeyType è il tipo di chiave che si sta utilizzando. Sostituire il tipo corretto!)

in alternativa, se è importante che la gemma viene rimosso prima chiamare OnGemCollected(), quindi (con chiave tipo TKey e valore di tipo TValue) fare in questo modo:

var itemsToRemove = new List<KeyValuePair<TKey, TValue>>(); 

foreach (var gem in gems) 
{ 
    gem.Value.Update(gameTime); 

    if (gem.Value.BoundingCircle.Intersects(Player.BoundingRectangle)) 
     itemsToRemove.Add(gem); 
} 

foreach (var item in itemsToRemove) 
{ 
    gems.Remove(item.Key); 
    OnGemCollected(item.Value, Player); 
} 
Problemi correlati