Domanda

Sto provando ad aggiornare un hashtable in un ciclo ma ricevo un errore: System.InvalidOperationException: Collection è stata modificata; l'operazione di enumerazione potrebbe non essere eseguita.

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

Esiste un modo per aggirarlo o forse esiste una struttura dati migliore a tale scopo?

È stato utile?

Soluzione

potresti prima leggere la raccolta di chiavi in ??un'altra istanza di IEnumerable, quindi cercare su quell'elenco

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

Altri suggerimenti

In teoria farei:

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

Se si utilizza un dizionario anziché un Hashtable, in modo che il tipo di chiavi sia noto, il modo più semplice per creare una copia della raccolta di chiavi per evitare questa eccezione è:

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

Perché ricevi un'eccezione che ti dice che hai modificato la raccolta su cui stai iterando, quando in realtà non l'hai fatto?

Internamente, la classe Hashtable ha un campo versione. I metodi Aggiungi, Inserisci e Rimuovi incrementano questa versione. Quando si crea un enumeratore su una delle raccolte esposte da Hashtable, l'oggetto enumeratore include la versione corrente di Hashtable. Il metodo MoveNext dell'enumeratore controlla la versione dell'enumeratore rispetto a quella di Hashtable e, se non sono uguali, genera l'invalidOperationException che stai vedendo.

Questo è un meccanismo molto semplice per determinare se Hashtable è stato modificato o meno. In effetti è un po 'troppo semplice. La collezione Keys dovrebbe davvero mantenere la propria versione e il suo metodo GetEnumerator dovrebbe salvare la versione della collezione nell'enumeratore, non la versione di Hashtable.

C'è un altro difetto di progettazione più sottile in questo approccio. La versione è un Int32. Il metodo UpdateVersion non controlla i limiti. È quindi possibile, se si esegue esattamente il numero corretto di modifiche a Hashtable (2 volte Int32.MaxValue , dare o prendere), affinché la versione su Hashtable e l'enumeratore siano uguali anche se hai cambiato radicalmente Hashtable dalla creazione dell'enumeratore. Quindi il metodo MoveNext non genererà l'eccezione, anche se dovrebbe, e otterrai risultati imprevisti.

Il modo più semplice è quello di copiare le chiavi in ??una raccolta separata, quindi scorrere invece quella.

Stai usando .NET 3.5? In tal caso, LINQ rende le cose un po 'più semplici.

La parte chiave è il metodo ToArray ()

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

Non è possibile modificare il set di elementi archiviati in una raccolta mentre ci si sta enumerando, poiché nella maggior parte dei casi la vita rende molto difficile l'iteratore. Considera il caso in cui la raccolta rappresenta un albero bilanciato e potrebbe essere soggetta a rotazioni dopo un inserimento. L'enumerato non avrebbe alcun modo plausibile per tenere traccia di ciò che ha visto.

Tuttavia, se stai solo cercando di aggiornare il valore, puoi scrivere:

deEntry.Value = sValue

L'aggiornamento del valore qui non ha alcun impatto sull'enumeratore.

Ecco come l'ho fatto all'interno di un dizionario; reimposta ogni valore in dict su 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;
}

Dipende dal motivo per cui esegui il ciclo tra gli elementi nella tabella hash. Ma probabilmente saresti in grado di scorrere le chiavi invece. Quindi

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

L'altra opzione è quella di creare una nuova HashTable. Scorrere il primo mentre si aggiungono elementi al secondo, quindi sostituire l'originale con quello nuovo.
Il ciclo tra le chiavi richiede tuttavia meno allocazioni di oggetti.

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

È uguale alle altre risposte, ma mi piace l'unica riga per ottenere le chiavi.

Converti in un array:

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

Forse puoi usare la raccolta Hashtable.Keys? L'enumerazione di ciò potrebbe essere possibile durante la modifica di Hashtable. Ma è solo una supposizione ...

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top