2008-11-28 67 views
9

我試圖更新循環中的哈希表,但得到一個錯誤:System.InvalidOperationException:集合被修改;枚舉操作可能不會執行。如何更新循環中的C#哈希表?

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

有沒有辦法解決這個問題,或者爲此目的可能有更好的數據結構?

+0

認爲,這是一個問題DUP看到:http://stackoverflow.com/questions/287195/how-to-add-items-to-a-collection -while-consume-it – 2008-11-28 22:03:20

回答

14

你能讀懂領取鑰匙到另一個IEnumerable的實例,然後再超過的foreach該列表

 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

這一個會做。謝謝! – 2008-11-28 22:36:42

1

在列舉集合時,您不能更改存儲在集合中的項集,因爲在大多數情況下這會使迭代器的生活變得非常困難。考慮集合代表平衡樹的情況,並且在插入後可能經歷旋轉。枚舉不會記錄它所看到的內容。

但是,如果你只是想更新的價值,那麼你可以這樣寫:

deEntry.Value = sValue 

更新的價值在這裏對枚舉沒有影響。

+1

那不能編譯:不能修改'deEntry'的成員,因爲它是'foreach迭代變量' – 2008-11-28 22:08:37

+1

這不會工作 – swordfish 2011-06-27 04:29:28

4

在概念上,我會做:

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

好的解決方案。謝謝。 – 2008-11-29 04:00:35

-4

也許你可以使用Hashtable.Keys收集?在改變Hashtable的同時枚舉可能是可能的。但是,這只是一個猜測...

+0

不,這不起作用。 – 2008-11-28 22:22:43

-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

產生相同的錯誤。 – 2008-11-28 22:17:14

+0

這就是在沒有先測試的情況下響應錯誤內存的問題。我再次想到這一點,我記得Hashtable使用相同的枚舉類型爲Hashtable和它的Keys。我認爲這是一個嚴重缺陷的實施。 – 2008-11-28 22:24:32

0

這取決於你爲什麼通過循環項目在散列表中。但是你可能會重複遍歷關鍵字。所以

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

另一種選擇是創建一個新的HashTable。在將項目添加到第二個項目時迭代第一個項目,然後用新項目替換原始項目。
儘管循環訪問鍵需要較少的對象分配。

2

最簡單的方法是將密鑰複製到一個單獨的集合中,然後遍歷它。

您使用的是.NET 3.5嗎?如果是這樣,LINQ使事情變得更容易一些。

3

如果您使用的是字典,而不是一個Hashtable,使鍵的類型是已知的,最簡單的方法,使鍵集合的副本,以避免這種情況的例外是:

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

爲什麼你會得到一個異常,告訴你你已經修改了你正在迭代的集合,而事實上你還沒有?

在內部,Hashtable類有一個版本字段。 Add,Insert和Remove方法遞增此版本。在Hashtable公開的任何集合上創建枚舉器時,枚舉器對象包含當前版本的Hashtable。枚舉器的MoveNext方法根據Hashtable檢查枚舉器的版本,如果它們不相等,則會拋出InvalidOperationException異常。

這是一個非常簡單的機制,用於確定散列表是否已被修改。其實這有點太簡單。 Keys集合真的應該保留自己的版本,它的GetEnumerator方法應該將集合的版本保存在枚舉器中,而不是Hashtable的版本。

這種方法還有另一個更微妙的設計缺陷。該版本是一個Int32。 UpdateVersion方法沒有邊界檢查。因此,如果您對Hashtable進行了正確的修改次數(2次Int32.MaxValue,給出或取),對於Hashtable上的版本和枚舉器是相同的,即使您自創建以來已經根本改變了Hashtable統計員。所以MoveNext方法不會拋出異常,即使它應該,你會得到意想不到的結果。

2

的關鍵部分是ToArray的()方法

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

這是我如何做到了在詞典中;在字典的每個值重置爲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) { 

這是一樣的其他的答案,但我喜歡的人行領取鑰匙。

0

將其轉換爲一個數組:

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