Pregunta

Estoy tratando de actualizar una tabla hash en un bucle pero obtengo un error: System.InvalidOperationException: se modificó la colección; la operación de enumeración puede no ejecutarse.

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 propósito?

¿Fue útil?

Solución

primero puede leer la colección de claves en otra instancia IEnumerable, y luego pasar por encima de 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]);
        }

Otros consejos

En concepto, 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;
}

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

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

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

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 Hashtable, el objeto enumerador incluye la versión actual de Hashtable. El método MoveNext del enumerador compara la versión del enumerador con la de Hashtable y, si no son iguales, arroja la InvalidOperationException que está viendo.

Este es un mecanismo muy simple para determinar si Hashtable ha sido modificado o no. 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 Hashtable.

Hay otro defecto de diseño más sutil en este enfoque. La versión es una Int32. El método UpdateVersion no verifica los límites. Por lo tanto, es posible, si realiza exactamente el número correcto de modificaciones en el Hashtable (2 veces Int32.MaxValue , give or take), para que la versión en el Hashtable y el enumerador sean los mismos aunque ha cambiado radicalmente la tabla hash desde que creó el enumerador. Por lo tanto, el método MoveNext no arrojará la excepción a pesar de que debería, y obtendrá resultados inesperados.

La forma 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.

La parte clave es el método ToArray ()

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

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

Sin embargo, si solo está tratando de actualizar el valor, puede escribir:

deEntry.Value = sValue

Actualizar el valor aquí no tiene ningún impacto en el enumerador.

Así es como lo hice dentro de un diccionario; restablece todos los valores en dict a 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;
}

Depende de por qué está recorriendo los elementos en la tabla hash. Pero probablemente podría iterar a través de las teclas en su lugar. 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. Repita el primero mientras agrega elementos al segundo y luego reemplace el original con el nuevo.
Sin embargo, recorrer las teclas requiere menos asignaciones de objetos.

List<string> keyList = htSettings_m.Keys.Cast<string>().ToList();
foreach (string key in keyList) {

Es lo mismo que las otras respuestas, pero me gusta que una línea obtenga las claves.

Conviértalo 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;
}
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;    
}

¿Tal vez puedas usar la colección Hashtable.Keys? Enumerar eso podría ser posible mientras se cambia el Hashtable. Pero es solo una suposición ...

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top