valor recorrer que programacion ejemplo diccionario buscar c# dictionary enumeration invalidoperationexception

c# - recorrer - ¿Por qué no podemos cambiar los valores de un diccionario mientras enumeramos sus claves?



que es un diccionario en python (9)

De la documentación (propiedad Dictionary.Item):

También puede usar la propiedad Elemento para agregar nuevos elementos al establecer el valor de una clave que no existe en el Diccionario. Cuando establece el valor de la propiedad, si la clave está en el Diccionario, el valor asociado con esa clave se reemplaza por el valor asignado. Si la clave no está en el Diccionario, la clave y el valor se agregan al diccionario. En contraste, el método Add no modifica los elementos existentes.

Así que, como indica John, no hay forma de que el marco sepa que no ha alterado los contenidos de la lista, por lo que se supone que sí.

class Program { static void Main(string[] args) { var dictionary = new Dictionary<string, int>() { {"1", 1}, {"2", 2}, {"3", 3} }; foreach (var s in dictionary.Keys) { // Throws the "Collection was modified exception..." on the next iteration // What''s up with that? dictionary[s] = 1; } } }

Entiendo completamente por qué se produce esta excepción al enumerar una lista; parece razonable esperar que durante la enumeración, la Estructura del objeto enumerado no cambie. Sin embargo, ¿cambiar un valor de un diccionario cambia su estructura ? En concreto, ¿la estructura de sus claves?


El indexador en el Dictionary es potencialmente una operación que puede cambiar la estructura de la colección, ya que agregará una nueva entrada con dicha clave si no existe una ya. Esto obviamente no es el caso aquí, pero espero que el contrato del Dictionary se mantenga deliberadamente simple, ya que todas las operaciones en el objeto se dividen en "mutantes" y "no mutantes", y todas las operaciones "mutantes" invalidan los enumeradores, incluso si En realidad no cambian nada.


Es porque diseñaron .Net con la capacidad de iterar una colección en varios subprocesos. Por lo tanto, debe permitir que el iterador sea multihilo o evitar eso y permitir la modificación de la colección durante la iteración, lo que requeriría limitar el objeto a iterar en un solo hilo. No puedo tener ambos.

De hecho, la respuesta a su pregunta es que el código que ingresa en realidad se traduce en una máquina de estado generada por el compilador ([CompilerGenerated]) que permite a los iteradores mantener el estado de recolección para proporcionar el rendimiento mágico. Es por eso que si no sincronizas tus colecciones y si iteras en un hilo y lo manipulas en otro, obtendrás un poco de mierda funky.

Echa un vistazo a: http://csharpindepth.com/articles/chapter6/iteratorblockimplementation.aspx

También: http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentHashMap.html "los iteradores están diseñados para ser usados ​​por un solo hilo a la vez".


Es posible que acabes de insertar una nueva clave en el diccionario, lo que de hecho cambiaría el dictionary.Keys . dictionary.Keys . Aunque en este bucle específico que nunca sucederá, la operación [] en general puede cambiar la lista de claves para que se marque como una mutación.


Gracias a Vitaliy volví y miré el código un poco más y parece que es una decisión de implementación específica rechazar esto (vea el fragmento a continuación). El Diccionario mantiene un valor privado llamado verrsion que se incrementa al cambiar el valor de un elemento existente. Cuando se crea el enumerador, toma nota del valor en ese momento y luego verifica cada llamada a MoveNext.

for (int i = this.buckets[index]; i >= 0; i = this.entries[i].next) { if ((this.entries[i].hashCode == num) && this.comparer.Equals(this.entries[i].key, key)) { if (add) { ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AddingDuplicate); } this.entries[i].value = value; this.version++; return; } }

No sé de una razón por la que esto sería necesario. Aún tiene la libertad de modificar las propiedades del valor, pero no lo asigne a un nuevo valor:

public class IntWrapper { public IntWrapper(int v) { Value = v; } public int Value { get; set; } } class Program { static void Main(string[] args) { var kvp = new KeyValuePair<string, int>("1",1); kvp.Value = 17; var dictionary = new Dictionary<string, IntWrapper>(){ {"1", new IntWrapper(1)}, {"2", new IntWrapper(2)}, {"3", new IntWrapper(3)} }; foreach (var s in dictionary.Keys) { dictionary[s].Value = 1; //OK dictionary[s] = new IntWrapper(1); // boom } } }


La respuesta corta es que está modificando la colección del diccionario, aunque no esté cambiando ninguna de sus claves. Por lo tanto, la próxima iteración que accede a la colección después de su actualización arroja una excepción que indica que la colección se modificó desde su último acceso (y con razón).

Para hacer lo que quiere, necesita una forma diferente de iterar a través de los elementos, de modo que al cambiarlos no se activará una excepción de iterador.


Para aquellos interesados ​​en cómo solucionar este problema, aquí hay una versión modificada del código de Vitaliy que funciona:

class Program { static void Main(string[] args) { var dictionary = new Dictionary<string, int>() { {"1", 1}, {"2", 2}, {"3", 3} }; string[] keyArray = new string[dictionary.Keys.Count]; dictionary.Keys.CopyTo(keyArray, 0); foreach (var s in keyArray) { dictionary[s] = 1; } } }

La respuesta es copiar las claves en otro enumerable y luego iterar sobre esa colección. Por alguna razón, no hay un método KeyCollection.ToList para facilitar las cosas. En su lugar, debe utilizar el método KeyCollection.CopyTo, que copia las claves en una matriz.


Porque los valores y las claves se almacenan como un par. No hay una estructura separada para claves y valores, sino una estructura única que almacena ambos como un conjunto de valores de pares. Cuando cambia un valor, es necesario cambiar la estructura subyacente única que contiene claves y valores.

¿Cambiar un valor cambia necesariamente el orden de la estructura subyacente? No. Pero este es un detalle específico de la implementación y la clase Dictionary<TKey,TValue> , correctamente, se considera que no revela esto al permitir la modificación de valores como parte de la API.


Veo de dónde vienes, en realidad. Lo que la mayoría de las respuestas aquí no notan es que usted está iterando en la lista de Claves, y no en los Elementos del Diccionario. Si los programadores de .NET framework quisieran, podrían diferenciar fácilmente entre los cambios realizados en la estructura del diccionario y los cambios realizados en los valores del diccionario. Sin embargo, incluso cuando las personas recorren las claves de una colección, por lo general terminan obteniendo los valores de todos modos. Sospecho que los diseñadores de .NET Framework pensaron que si está iterando a través de esos valores, querrá saber si algo los está cambiando por debajo de usted, tanto como con cualquier Lista. O eso o no lo vieron como un problema lo suficientemente importante como para merecer la programación y el mantenimiento que se requeriría para diferenciar entre un tipo de cambio y otro.