2010-11-15 13 views
9

He declarado un objeto de tipo Diccionario e intento agregar algunos elementos en él. Pero no puedo modificar ni siquiera el valor del artículo. Es razonable que la clave no sea modificable, pero ¿por qué no el valor? Gracias.¿Por qué el diccionario .NET <TKey, TValue> es inmutable?

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 
    } 

Respuesta

14

El diccionario en sí no es inmutable. Pero no puede cambiar el diccionario mientras lo enumera en el bucle foreach.

Aquí un enlace para la lectura adicional: Why is The Iteration Variable in a C# foreach statement read-only?

+2

En otras palabras, una colección actualmente iterada es inmutable – abatishchev

+1

@abatishchev, hay una diferencia entre un objeto que no se debe modificar y un objeto que no se puede modificar. El término _immutable_ se refiere a este último, mientras que una colección que se está iterando es la primera. –

17

No está permitido modificar la colección que se está interactuando sobre foreach en un bucle. Esto no tiene nada que ver con el diccionario: dict[key] = "value"; funciona perfectamente bien fuera de foreach bucles.

2

Dictionary es no inmutable (en primer lugar, si lo fuera, no serías capaz de Add a ella).

El problema es que usted está tratando de modificar la colección al iterar sobre ella - no se puede cambiar una colección dentro de un bloque foreach usarlo.

Cambiar los valores fuera de foreach funcionará.

+0

No puede acceder a un diccionario utilizando un índice 'int', a menos que en realidad sea un' Diccionario '. – LukeH

+0

@LukeH - muy bien. Estaba pensando en otras colecciones ... – Oded

4

No es inmutable, se puede cambiar, es sólo que el repetidor actual no es válida si se modifica la colección. Se puede "resolver" esta forzando el iterador para completar antes de modificar la colección

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

Usted está tratando de modificar la colección, al mismo tiempo que está iterando sobre el mismo. Eso no está permitido. La excepción es lanzada por el siguiente método de movimiento del enumerador en el bucle foreach.

1

Como mencionó @Femaref, no puede cambiar el diccionario mientras itera dentro de un bucle foreach. Otra solución es utilizar para el bucle y pega el siguiente código para cambiarlo

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

Nota: Usted tiene que usar LINQ para esto.

+0

Ineficiente para diccionarios grandes, pero probablemente viable. –

+0

Los dicaticadores no están ordenados. Esto puede fallar horriblemente en algunos casos. – SLaks

+0

como tal .... ??? Esta lógica es independiente del orden de los elementos en el diccionario. –

2

Una analogía que puedo pensar es tratar de cortar una rama de un árbol mientras estás sentado sobre ella. Por lo tanto, se considera inseguro.

Una alternativa podría ser utilizar el bucle for o clonar la lista.

1

Convertir dict.Keys en una matriz.

Luego haga eso para el ciclo.

Si modifica la colección arrojará una excepción.

7

Está utilizando el término immutable incorrecto. Se usa comúnmente para objetos cuyo estado interno no cambiará una vez que se hayan inicializado. El Dictionary<TKey, TValue> es mutable como puede ser. Que arroje excepciones cuando alguien intenta modificarlo durante la enumeración es un comportamiento explícitamente implementado.

Probablemente esté confundido sobre por qué la enumeración cambiará cuando solo cambie un valor. Esto no está cubierto por las otras respuestas todavía.

Bueno, cuando lo hace

dict[key] = "something"; 

la enumeración se cambio si la clave no hace aún existir, ya que se añadirá en ese caso, haciendo que la enumeración de cambiar. Seguramente, la implementación del diccionario podría verificar eso primero y permitir la modificación si la clave ya existe, pero supongo que sigue el principio Falló temprano, falló a menudo para mejorar la integridad global de las aplicaciones.

1

Microsoft decidió que los objetos iEnumerable que admiten un método de reinicio deben devolver exactamente los mismos datos si se realizan múltiples pases a través de ellos. Esta es una garantía útil si uno desea, p. producir una cadena que contenga todos los elementos (un primer pase podría contar la longitud de todos los artículos, y un segundo pase podría concatenarlos). Dado que incluso cambiar el valor de un elemento del diccionario violaría esa garantía, está prohibido. Hubiera sido agradable tener cambios en el valor de un diccionario, solo causar un chillido si, de hecho, se intenta un segundo pase a través de una colección, pero no es así como Microsoft diseñó las cosas. Además, no estoy familiarizado con la implementación interna del diccionario, pero es posible que maneje un cambio de valor como eliminación e inserción. Si hace eso, eso ciertamente rompería un enumerador.

Cuestiones relacionadas