2010-11-15 23 views
9

Ho dichiarato un oggetto tipo dizionario e provo ad aggiungervi alcuni elementi. Ma non posso modificare neanche il valore dell'oggetto. È ragionevole che la chiave non sia modificabile, ma perché non il valore? Grazie.Perché il dizionario .NET <TKey, TValue> è immutabile?

Dictionary<string, string> dict = new Dictionary<string, string>(); 
    dict.Add("1", "bob"); 
    dict.Add("2", "jack"); 
    dict.Add("3", "wtf"); 
    foreach (string key in dict.Keys) 
    { 
     dict[key] = "changed"; //System.InvalidOperationException: Collection was modified 
    } 

risposta

14

Il dizionario stesso non è immutabile. Ma non è possibile cambiare il dizionario mentre lo si enumera nel ciclo foreach.

Ecco un link per ulteriori letture: Why is The Iteration Variable in a C# foreach statement read-only?

+2

In altre parole, una collezione attualmente iterata è immutabile – abatishchev

+1

@abatishchev, c'è una differenza tra un oggetto che non dovrebbe essere modificato e un oggetto che non può essere modificato. Il termine _immutabile_ si riferisce a quest'ultimo, mentre un insieme che viene ripetuto è il primo. –

17

Non è consentito modificare la raccolta si effettua l'iterazione in un ciclo foreach. Questo non ha nulla a che fare con il dizionario - dict[key] = "value"; funziona perfettamente bene fuori dallo foreach loop.

2

Dictionary è non immutabili (prima, se lo fosse, non sarebbe in grado di Add ad esso).

Il problema è che si sta tentando di modificare l'insieme mentre l'iterazione su di esso - non è possibile modificare una collezione all'interno di un blocco foreach usarlo.

La modifica dei valori al di fuori di foreach funzionerà.

+0

Non è possibile accedere a un dizionario utilizzando un indice 'int', a meno che non sia in realtà un' dizionario '. – LukeH

+0

@LukeH - giustissimo. Stavo pensando ad altre collezioni ... – Oded

4

E non è immutabile, si può cambiare, è solo che l'iteratore corrente non è più valido se si modifica la collezione. È possibile "risolvere" questo forzando l'iteratore per completare prima di modificare la raccolta

foreach (string key in new List<string>(dict.Keys)) 
{ 
    dict[key] = "changed"; //System.InvalidOperationException: Collection was modified 
} 
1

Si sta cercando di modificare l'insieme allo stesso tempo si sta iterazione su di esso. Questo non è permesso. L'eccezione viene generata dallo spostamento del metodo successivo dell'enumeratore nel ciclo foreach.

1

Come @Femaref menzionato, non è possibile modificare il dizionario durante l'iterazione all'interno di un ciclo foreach. Un altro lavoro intorno è quello di utilizzare per il ciclo e utilizzare il seguente codice di cambiarlo

for (int i = 0; i < dict.Count; i++) 
{ 
    var key = dict.Keys.ElementAt(i); 
    dict[key] = "changed"; 
} 

Nota: Devi usare LINQ per questo.

+0

Inefficiente per dizionari di grandi dimensioni, ma probabilmente fattibile. –

+0

I dizionari non sono ordinati. Questo può fallire in modo orribile in alcuni casi. – SLaks

+0

come tale .... ??? Questa logica è indipendente dall'ordine degli elementi nel dizionario. –

2

Un'analogia a cui riesco a pensare sta tentando di tagliare un ramo di un albero mentre ci si siede su di esso. Quindi è considerato pericoloso.

Un'alternativa potrebbe essere quella di utilizzare il ciclo for o clonare l'elenco.

1

Convertire dict.Keys in serie.

Quindi fallo per il ciclo.

Se si modifica la raccolta, verrà generata un'eccezione.

7

Si sta usando il termine immutable errato. È comunemente usato per oggetti che lo stato interno non cambierà una volta inizializzati. Dictionary<TKey, TValue> è modificabile come può essere. Che genera eccezioni quando qualcuno tenta di modificarlo durante l'enumerazione è un comportamento esplicitamente implementato.

Probabilmente sei confuso perché l'enumerazione cambierà quando cambi solo un valore. Questo non è ancora coperto dalle altre risposte.

Beh, quando si fanno

dict[key] = "something"; 

l'enumerazione sarà cambiamento se la chiave non non esiste ancora, perché sarà aggiunto in questo caso, causando l'enumerazione di cambiare. Sicuramente, l'implementazione del dizionario potrebbe controllarla prima e consentire la modifica se la chiave esiste già, ma suppongo che segua il principio Fail early, fail spesso per migliorare l'integrità complessiva delle applicazioni.

1

Microsoft ha deciso che gli oggetti iEnumerable che supportano un metodo di ripristino dovrebbero restituire esattamente gli stessi dati se sono stati effettuati più passaggi. Questa è una garanzia utile se si desidera ad es. produrre una stringa contenente tutti gli elementi (una prima passata potrebbe compensare la lunghezza di tutti gli articoli e una seconda passata potrebbe concatenarli). Poiché anche la modifica del valore di un elemento del dizionario violerebbe tale garanzia, è vietata. Sarebbe stato bello che le modifiche al valore di un dizionario causassero solo uno squawk se in effetti si tentasse un secondo passaggio attraverso una raccolta, ma non è così che Microsoft ha progettato le cose. Inoltre, non ho familiarità con l'implementazione interna del dizionario, ma è possibile che gestisca una modifica del valore come cancellazione e inserimento. Se lo facesse, ciò avrebbe sicuramente rotto un enumeratore.

Problemi correlati