2008-11-28 50 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; 
}