2008-11-28 23 views
9

Estoy tratando de actualizar una tabla hash en un bucle pero obteniendo un error: System.InvalidOperationException: la colección se modificó; la operación de enumeración no se puede ejecutar.¿Cómo actualizar la hashtable C# en un bucle?

private Hashtable htSettings_m = new Hashtable(); 
htSettings_m.Add("SizeWidth", "728"); 
htSettings_m.Add("SizeHeight", "450"); 
string sKey = ""; 
string sValue = ""; 
foreach (DictionaryEntry deEntry in htSettings_m) 
{ 
    // Get value from Registry and assign to sValue. 
    // ... 
    // Change value in hashtable. 
    sKey = deEntry.Key.ToString(); 
    htSettings_m[sKey] = sValue; 
} 

¿Hay alguna forma de evitarlo o tal vez hay una mejor estructura de datos para tal fin?

+0

creen que esto es una cuestión de la duplicación ver: http://stackoverflow.com/questions/287195/how-to-add-items-to-a-collection -mientras lo consume –

Respuesta

14

se podía leer la recogida de llaves en otro caso IEnumerable en primer lugar, a continuación, forEach sobre esa lista

 System.Collections.Hashtable ht = new System.Collections.Hashtable(); 

     ht.Add("test1", "test2"); 
     ht.Add("test3", "test4"); 

     List<string> keys = new List<string>(); 
     foreach (System.Collections.DictionaryEntry de in ht) 
      keys.Add(de.Key.ToString()); 

     foreach(string key in keys) 
     { 
      ht[key] = DateTime.Now; 
      Console.WriteLine(ht[key]); 
     } 
+0

Éste lo hará. ¡Gracias! –

1

No puede cambiar el conjunto de elementos almacenados en una colección mientras está enumerando sobre ella, ya que eso hace la vida muy difícil para el iterador en la mayoría de los casos. Considere el caso en que la colección representa un árbol equilibrado, y bien puede sufrir rotaciones después de un inserto. El enumerado no tendría una forma plausible de hacer un seguimiento de lo que ha visto.

Sin embargo, si usted está tratando de actualizar el valor, entonces se puede escribir:

deEntry.Value = sValue 

actualizar el valor que aquí no tiene impacto en el empadronador.

+1

Eso no se compila: no se pueden modificar los miembros de 'deEntry' porque es una 'variable de iteración foreach' –

+1

Esto tampoco funcionó – swordfish

4

En concepto que haría:

Hashtable table = new Hashtable(); // ps, I would prefer the generic dictionary.. 
Hashtable updates = new Hashtable(); 

foreach (DictionaryEntry entry in table) 
{ 
    // logic if something needs to change or nog 
    if (needsUpdate) 
    { 
     updates.Add(key, newValue); 
    } 
} 

// now do the actual update 
foreach (DictionaryEntry upd in updates) 
{ 
    table[upd.Key] = upd.Value; 
} 
+0

Buena solución también. Gracias. –

-4

Tal vez usted puede utilizar la colección Hashtable.Keys? Enumerando a través de eso podría ser posible al cambiar la Hashtable. Pero es sólo una suposición ...

+0

No, eso no funciona. –

-1
private Hashtable htSettings_m = new Hashtable(); 

htSettings_m.Add("SizeWidth", "728");  
htSettings_m.Add("SizeHeight", "450");  
string sValue = "";  
foreach (string sKey in htSettings_m.Keys)  
{  
    // Get value from Registry and assign to sValue  
    // ...  
    // Change value in hashtable.  
    htSettings_m[sKey] = sValue;  
} 
+0

Produce el mismo error. –

+0

Ese es el problema de responder desde una memoria defectuosa sin probar primero. Pensé en esto otra vez y recordé que Hashtable usa el mismo tipo de enumerador para una Hashtable y para sus Keys. Una implementación seriamente defectuosa en mi opinión. –

+0

No, el problema no es el tipo de enumerador, es que la propiedad Keys no toma una * copia * de todas las claves, simplemente itera sobre la colección subyacente. Cuando * necesita * tomar una copia, hágalo explícitamente. Es el comportamiento que quiero y espero, personalmente. –

0

Depende de qué se recorre los artículos en la tabla hash. Pero probablemente puedas iterar a través de las teclas. Entonces

foreach (String sKey in htSettings_m.Keys) 
{ // Get value from Registry and assign to sValue. 
    // ...  
    // Change value in hashtable. 
    htSettings_m[sKey] = sValue; 
} 

La otra opción es crear una nueva HashTable. Itere a través del primero mientras agrega elementos al segundo y luego reemplace el original por el nuevo.
Sin embargo, el bucle a través de las teclas requiere menos asignaciones de objetos.

2

La manera más simple es copiar las claves en una colección separada, y luego repetirlas en su lugar.

¿Está utilizando .NET 3.5? Si es así, LINQ hace las cosas un poco más fáciles.

3

Si estás utilizando un diccionario en lugar de una tabla hash, por lo que el tipo de las claves es conocido, la forma más fácil de hacer una copia de la colección Claves para evitar esta excepción es:

foreach (string key in new List<string>(dictionary.Keys)) 

¿Por qué recibes una excepción diciéndote que has modificado la colección sobre la que estás iterando, cuando en realidad no la has modificado?

Internamente, la clase Hashtable tiene un campo de versión. Los métodos Agregar, Insertar y Eliminar incrementan esta versión. Cuando crea un enumerador en cualquiera de las colecciones que expone el Hashtable, el objeto enumerador incluye la versión actual de Hashtable. El método MoveNext del enumerador comprueba la versión del enumerador frente a la Hashtable, y si no son iguales, arroja la InvalidOperationException que está viendo.

Este es un mecanismo muy simple para determinar si la Hashtable ha sido o no modificada. De hecho, es un poco demasiado simple. La colección Keys realmente debería mantener su propia versión, y su método GetEnumerator debería guardar la versión de la colección en el enumerador, no la versión de la Hashtable.

Hay otro defecto de diseño más sutil en este enfoque. La versión es un Int32. El método UpdateVersion no verifica límites. Por lo tanto, es posible, si realiza exactamente el número correcto de modificaciones en el Hashtable (2 veces Int32.MaxValue, dar o recibir), para que la versión en el Hashtable y el enumerador sea la misma aunque haya cambiado radicalmente el Hashtable desde la creación el enumerador Por lo tanto, el método MoveNext no arrojará la excepción aunque debería hacerlo, y obtendrá resultados inesperados.

2

La parte clave es la ToArray() método

var dictionary = new Dictionary<string, string>(); 
foreach(var key in dictionary.Keys.ToArray()) 
{ 
    dictionary[key] = "new value"; 
} 
+0

solución mucho más simple que la superior actual. – Lars

0

Ésta es la forma en que lo hice dentro de un diccionario; restablece todos los valores de dict false:

Dictionary<string,bool> dict = new Dictionary<string,bool>(); 

for (int i = 0; i < dict.Count; i++) 
{ 
    string key = dict.ElementAt(i).Key; 
    dict[key] = false; 
} 
0
List<string> keyList = htSettings_m.Keys.Cast<string>().ToList(); 
foreach (string key in keyList) { 

Es lo mismo que las otras respuestas, pero me gusta la una línea para conseguir las llaves.

+1

Esto sería mejor como un comentario sobre la respuesta aceptada. –

0

convertirlo en una matriz:

private Hashtable htSettings_m = new Hashtable(); 
htSettings_m.Add("SizeWidth", "728"); 
htSettings_m.Add("SizeHeight", "450"); 
string sKey = ""; 
string sValue = ""; 

ArrayList htSettings_ary = new ArrayList(htSettings_m.Keys) 
foreach (DictionaryEntry deEntry in htSettings_ary) 
{ 
    // Get value from Registry and assign to sValue. 
    // ... 
    // Change value in hashtable. 
    sKey = deEntry.Key.ToString(); 
    htSettings_m[sKey] = sValue; 
} 
Cuestiones relacionadas