2008-11-28 7 views
9

Ich versuche, eine Hashtabelle in einer Schleife zu aktualisieren, aber einen Fehler zu erhalten: System.InvalidOperationException: Collection wurde geändert; Aufzählungsoperation wird möglicherweise nicht ausgeführt.Wie aktualisiere ich C# Hashtable in einer Schleife?

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

Gibt es einen Weg dahin oder vielleicht gibt es eine bessere Datenstruktur für diesen Zweck?

+0

glauben, dass dies eine Frage dup zu sehen ist: http://stackoverflow.com/questions/287195/how-to-add-items-to-a-collection -while-consuming-it –

Antwort

14

Sie die Sammlung von Schlüsseln in einer anderen IEnumerable Instanz zuerst lesen konnten, dann foreach über diese 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]); 
     } 
+0

Dieser wird es tun. Vielen Dank! –

1

Sie können die Menge der in einer Sammlung gespeicherten Elemente nicht ändern, während Sie darüber aufzählen, da dies das Leben für den Iterator in den meisten Fällen sehr schwierig macht. Betrachten Sie den Fall, in dem die Sammlung einen ausgeglichenen Baum darstellt und nach einer Insertion durchaus Rotationen erfahren kann. Die Aufzählung hätte keine plausible Möglichkeit, den Überblick über das zu behalten, was sie gesehen hat.

Wenn Sie jedoch nur versuchen, den Wert zu aktualisieren, dann können Sie schreiben:

deEntry.Value = sValue 

den Wert hier Aktualisierung hat keine Auswirkungen auf die enumerator.

+1

Das kompiliert nicht: Mitglieder von 'deEntry' können nicht geändert werden, weil es eine 'Foreach-Iterationsvariable' ist –

+1

Das würde nicht funktionieren – swordfish

4

Im Konzept würde ich tun:

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

Gute Lösung auch. Vielen Dank. –

-4

Vielleicht können Sie Hashtable.Keys Sammlung verwenden? Aufzählung könnte möglich sein, während die Hashtabelle geändert wird. Aber es ist nur eine Vermutung ...

+0

Nein, das funktioniert nicht. –

-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

Erzeugt denselben Fehler. –

+0

Das ist das Problem mit der Reaktion von fehlerhaftem Speicher ohne zuerst zu testen. Ich dachte nochmal darüber nach und erinnerte mich, dass Hashtable den gleichen Enumerator-Typ für eine Hashtable und für seine Schlüssel verwendet. Eine ernsthaft fehlerhafte Umsetzung meiner Meinung nach. –

+0

Nein, das Problem ist nicht der Aufzählungstyp - es ist, dass die Keys-Eigenschaft keine * Kopie * aller Schlüssel benötigt, sondern nur über die zugrunde liegende Sammlung iteriert. Wenn Sie * eine Kopie benötigen, tun Sie dies ausdrücklich. Es ist das Verhalten, das ich persönlich will und erwarte. –

0

Es hängt davon ab, warum Sie die Einzelteile sind Schleifen in der Hashtabelle. Aber Sie könnten wahrscheinlich stattdessen durch die Schlüssel iterieren. So

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

Die andere Möglichkeit ist, eine neue HashTable zu erstellen. Durchlaufen Sie die erste, während Sie der zweiten Komponente Elemente hinzufügen, und ersetzen Sie dann das Original durch das neue.
Das Durchlaufen der Schlüssel erfordert jedoch weniger Objektzuweisungen.

2

Der einfachste Weg ist es, die Schlüssel in eine separate Sammlung zu kopieren, dann iterieren Sie stattdessen.

Verwenden Sie .NET 3.5? Wenn ja, macht LINQ die Dinge ein bisschen einfacher.

3

Wenn Sie ein Wörterbuch anstelle eines Hashtable verwenden, so dass die Art der Schlüssel bekannt ist, ist der einfachste Weg, um eine Kopie der Schlüssel Sammlung zu machen, diese Ausnahme zu vermeiden ist:

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

Warum erhalten Sie eine Ausnahme, die Ihnen mitteilt, dass Sie die Sammlung, über die Sie iterieren, geändert haben, obwohl Sie dies tatsächlich nicht getan haben?

Intern besitzt die Hashtable-Klasse ein Versionsfeld. Die Methoden Hinzufügen, Einfügen und Entfernen inkrementieren diese Version. Wenn Sie einen Enumerator für eine der Sammlungen erstellen, die die Hashtable verfügbar macht, enthält das Enumerator-Objekt die aktuelle Version der Hashtable. Die MoveNext-Methode des Enumerators prüft die Version des Enumerators anhand der Hashtable, und wenn sie nicht gleich sind, wird die angezeigte InvalidOperationException ausgelöst.

Dies ist ein sehr einfacher Mechanismus zur Bestimmung, ob die Hashtable geändert wurde oder nicht. In der Tat ist es ein bisschen zu einfach. Die Keys-Sammlung sollte eigentlich eine eigene Version beibehalten, und die GetEnumerator-Methode sollte die Version der Sammlung im Enumerator speichern, nicht die Version der Hashtable.

Bei diesem Ansatz gibt es einen weiteren, subtileren Konstruktionsfehler. Die Version ist ein Int32. Die UpdateVersion-Methode überprüft keine Grenzen. Es ist daher möglich, wenn Sie genau die richtige Anzahl von Änderungen an der Hashtable machen (2 mal Int32.MaxValue, geben oder nehmen), damit die Version auf Hashtable und Enumerator gleich bleibt, obwohl Sie die Hashtable seit der Erstellung radikal geändert haben der Enumerator. Daher wird die MoveNext-Methode die Ausnahme nicht auslösen, obwohl dies erforderlich ist, und Sie erhalten unerwartete Ergebnisse.

2

Der wichtigste Teil ist die ToArray() Methode

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

viel einfachere Lösung als die aktuelle Top. – Lars

0

Dies ist, wie ich es in einem Wörterbuch tat; jeden Wert in dict auf false setzt:

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 ist das gleiche wie die anderen Antworten, aber Ich mag die eine Zeile die Schlüssel zu bekommen.

+1

Dies wäre besser als ein Kommentar auf die angenommene Antwort. –

0

wandelt es in ein 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; 
}