繁体   English   中英

如何在循环中更新C#哈希表?

[英]How to update C# hashtable in a loop?

我正在尝试更新循环中的哈希表,但收到错误:System.InvalidOperationException:Collection已被修改; 枚举操作可能无法执行。

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

有没有方法或者为此目的有更好的数据结构?

你可以先将密钥集合读入另一个IEnumerable实例,然后再遍历该列表

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

在概念中,我会这样做:

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

如果您使用的是Dictionary而不是Hashtable,那么键的类型是已知的,那么复制Keys集合以避免此异常的最简单方法是:

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

你为什么得到一个例外,告诉你你已经修改了你正在迭代的集合,而事实上你还没有?

在内部,Hashtable类有一个版本字段。 Add,Insert和Remove方法会增加此版本。 在Hashtable公开的任何集合上创建枚举器时,枚举器对象包括Hashtable的当前版本。 枚举器的MoveNext方法根据Hashtable检查枚举器的版本,如果它们不相等,则抛出您看到的InvalidOperationException。

这是一种非常简单的机制,用于确定Hashtable是否已被修改。 事实上它有点太简单了。 Keys集合确实应该维护自己的版本,它的GetEnumerator方法应该在枚举器中保存集合的版本,而不是Hashtable的版本。

这种方法还有另一个更微妙的设计缺陷。 该版本是Int32。 UpdateVersion方法不进行边界检查。 因此,如果您对Hashtable进行了恰当数量的修改(2次Int32.MaxValue ,给予或采取),则Hashtable和枚举器上的版本即使您彻底改变了Hashtable也是可能的自从创建枚举器。 所以MoveNext方法即使应该抛出异常也不会抛出异常,你会得到意想不到的结果。

关键部分是ToArray()方法

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

最简单的方法是将密钥复制到一个单独的集合中,然后迭代它。

你在使用.NET 3.5吗? 如果是这样,LINQ使事情变得更容易一些。

在枚举时,您无法更改存储在集合中的项集,因为在大多数情况下,这会使迭代器的生命变得非常困难。 考虑集合表示平衡树的情况,并且在插入之后可能经历旋转。 枚举将没有合理的方式来跟踪它所看到的内容。

但是,如果您只是尝试更新该值,那么您可以编写:

deEntry.Value = sValue

在此更新值对枚举器没有影响。

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

它与其他答案相同,但我喜欢一行来获取密钥。

将其转换为数组:

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

这取决于您循环遍历哈希表中的项目的原因。 但是你可能可以通过密钥迭代。 所以

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

另一种选择是创建一个新的HashTable。 迭代第一个,同时将项目添加到第二个,然后用新的替换原始。
循环使用密钥需要较少的对象分配。

这就是我在字典中的做法; 将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;
}
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;    
}

也许你可以使用Hashtable.Keys集合? 在更改Hashtable时可能可以枚举。 但这只是猜测......

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM