【问题标题】:How to update C# hashtable in a loop?如何在循环中更新 C# 哈希表?
【发布时间】:2010-09-24 12:21:09
【问题描述】:

我正在尝试在循环中更新哈希表,但出现错误:System.InvalidOperationException: Collection was modified;枚举操作可能无法执行。

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

有没有办法解决这个问题,或者是否有更好的数据结构用于此目的?

【问题讨论】:

标签: c# data-structures loops hashtable


【解决方案1】:

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

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

deEntry.Value = sValue

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

【讨论】:

  • 无法编译:无法修改“deEntry”的成员,因为它是“foreach 迭代变量”
【解决方案2】:

在概念上我会这样做:

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      您可以先将键集合读入另一个 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]);
              }
      

      【讨论】:

        【解决方案5】:
        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 对 Hashtable 和它的 Keys 使用相同的枚举器类型。在我看来,这是一个有严重缺陷的实现。
        • 不,问题不在于枚举器类型 - 而是 Keys 属性不获取所有键的 副本,它只是遍历基础集合。当您需要复制时,请明确地这样做。这是我个人想要和期望的行为。
        • 在这里我不得不不同意你的观点:Keys 集合是 Keys 集合而不是 Hashtable,Keys 枚举器应该只对 Keys 集合中的变化敏感,而不是对整个 Hashtable 中的变化敏感。
        • 我认为这是 Hashtable 类的设计缺陷。在我的回答中查看更多详细信息。
        【解决方案6】:

        这取决于您为什么要遍历哈希表中的项目。但是您可能可以遍历键。所以

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

        另一个选项是创建一个新的 HashTable。迭代第一个,同时将项目添加到第二个,然后用新的替换原来的。
        不过,遍历键需要较少的对象分配。

        【讨论】:

          【解决方案7】:

          最简单的方法是将键复制到一个单独的集合中,然后对其进行迭代。

          您使用的是 .NET 3.5 吗?如果是这样,LINQ 会让事情变得简单一些。

          【讨论】:

            【解决方案8】:

            如果您使用字典而不是哈希表,以便知道键的类型,则复制 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 方法也不会抛出异常,你会得到意想不到的结果。

            【讨论】:

              【解决方案9】:

              关键部分是ToArray()方法

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

              【讨论】:

              • 比当前最简单的解决方案。
              【解决方案10】:

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

              【讨论】:

                【解决方案11】:
                List<string> keyList = htSettings_m.Keys.Cast<string>().ToList();
                foreach (string key in keyList) {
                

                和其他答案一样,但我喜欢用一行来获取钥匙。

                【讨论】:

                  【解决方案12】:

                  将其转换为数组:

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

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2012-03-05
                    • 1970-01-01
                    • 2013-02-03
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2014-11-28
                    相关资源
                    最近更新 更多