Question

J'essaie de mettre à jour une table de hachage dans une boucle, mais une erreur s'est produite: System.InvalidOperationException: La collection a été modifiée. l'opération d'énumération peut ne pas s'exécuter.

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;
}

Y at-il un moyen de contourner le problème ou peut-être existe-t-il une meilleure structure de données à cette fin?

Était-ce utile?

La solution

vous pouvez d'abord lire la collection de clés dans une autre instance IEnumerable, puis sur chaque liste

        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]);
        }

Autres conseils

Dans le concept que je ferais:

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 vous utilisez un dictionnaire au lieu d'une table de hachage afin de connaître le type de clé, le moyen le plus simple de créer une copie de la collection Keys pour éviter cette exception est:

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

Pourquoi obtenez-vous une exception vous disant que vous avez modifié la collection sur laquelle vous effectuez une itération, alors que ce n'est pas le cas?

En interne, la classe Hashtable a un champ de version. Les méthodes Add, Insert et Remove incrémentent cette version. Lorsque vous créez un énumérateur sur l'une des collections exposées par la table de hachage, l'objet énumérateur comprend la version actuelle de la table de hachage. La méthode MoveNext de l'énumérateur compare la version de l'énumérateur à celle de la table de hachage et, si elles ne sont pas égales, elle lève l'exception InvalidOperationException que vous voyez.

Il s'agit d'un mécanisme très simple permettant de déterminer si la table de hachage a été modifiée ou non. En fait, c'est un peu trop simple. La collection Keys doit réellement conserver sa propre version et sa méthode GetEnumerator doit enregistrer la version de la collection dans l'énumérateur, et non la version de Hashtable.

Cette approche présente un autre défaut de conception plus subtil. La version est un Int32. La méthode UpdateVersion ne vérifie pas les limites. Il est donc possible, si vous apportez exactement le bon nombre de modifications à la table de hachage (2 fois Int32.MaxValue , donner ou prendre), que la version de la table de hachage et l'énumérateur soient identiques même si vous avez radicalement changé la table de hachage depuis la création de l'énumérateur. Ainsi, la méthode MoveNext ne lève pas l’exception même s’il le devrait, et vous obtiendrez des résultats inattendus.

La méthode la plus simple consiste à copier les clés dans une collection distincte, puis à les parcourir.

Utilisez-vous .NET 3.5? Si tel est le cas, LINQ facilite un peu les choses.

L'élément clé est la méthode ToArray ()

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

Vous ne pouvez pas modifier l'ensemble d'éléments stockés dans une collection pendant l'énumération, car cela rend la vie très difficile pour l'itérateur dans la plupart des cas. Prenons le cas où la collection représente un arbre équilibré et peut bien subir des rotations après un insert. L’énumération n’aurait aucun moyen plausible de garder une trace de ce qu’elle a vu.

Cependant, si vous essayez simplement de mettre à jour la valeur, vous pouvez écrire:

deEntry.Value = sValue

La mise à jour de la valeur ici n'a aucun impact sur l'énumérateur.

Voici comment je l'ai fait dans un dictionnaire. réinitialise toutes les valeurs 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;
}

Cela dépend de la raison pour laquelle vous parcourez les éléments de la table de hachage. Mais vous seriez probablement capable de parcourir les clés à la place. Donc

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

L’autre option consiste à créer une nouvelle table de hachage. Parcourez le premier tout en ajoutant des éléments au second, puis remplacez l'original par le nouveau.
La boucle à travers les clés nécessite cependant moins d’allocations d’objets.

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

C’est la même chose que les autres réponses, mais j’aime bien qu’une seule ligne obtienne les clés.

Convertissez-le en tableau:

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;    
}

Peut-être pouvez-vous utiliser la collection Hashtable.Keys? Enumérer à travers cela pourrait être possible en changeant la Hashtable. Mais ce n’est qu’une hypothèse ...

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top