【问题标题】:Collection was modified; enumeration operation may not execute for hashtable [duplicate]收藏已修改;可能无法对哈希表执行枚举操作[重复]
【发布时间】:2025-12-13 11:30:01
【问题描述】:

我有这个定时器功能,它给了我以下异常。
收藏已修改;枚举操作可能无法执行 一旦我从哈希表中删除对象。

实现类似功能的解决方案是什么

void timerFunction(object data)
    {
    lock (tMap.SyncRoot)
    {
      foreach (UInt32 id in tMap.Keys)
      {
         MyObj obj=(MyObj) runScriptMap[id];
         obj.time = obj.time -1;
         if (obj.time <= 0)
         {                      
            tMap.Remove(id);
         }
       }
    }

【问题讨论】:

    标签: c# enumeration


    【解决方案1】:

    传统的方法是,在您的 foreach 循环中,将要删除的项目收集到一个列表中。然后,一旦 foreach 循环完成,在该 List 上执行另一个 foreach(这次 不是 超过 tMap),调用 tMap.Remove:

    void timerFunction(object data)
    {
      lock (tMap.SyncRoot)
      {
        List<UInt32> toRemove = new List<UInt32>();
    
        foreach (UInt32 id in tMap.Keys)
        {
          MyObj obj=(MyObj) runScriptMap[id];
          obj.time = obj.time -1;
          if (obj.time <= 0)
          {                      
            toRemove.Add(id);
          }
        }
    
        foreach (UInt32 id in toRemove)
        {
          tMap.Remove(id);
        }
      }
    }
    

    【讨论】:

    • 值得指出的是,线程安全在这里并不是一个真正的因素。无论是一个线程还是多个线程在枚举中干预您的集合,您都会收到相同的错误。 (虽然我当然明白你为什么要将它限制在一个线程中。)
    【解决方案2】:

    http://social.msdn.microsoft.com/Forums/en-IE/csharplanguage/thread/519c6abc-5b5b-457a-adfb-251ee013d123

         List<string> tablesNamesToRemove = new List<string>();
         foreach (string ikey in gridNum.Keys)
         {
            Hashtable aux = (Hashtable)gridNum[ikey];
            if (aux["OK"].ToString().Trim() == "1")
               tablesNamesToRemove.Add(ikey);
         }
    
         foreach (string name in tablesNamesToRemove)
         {
            gridNum.Remove(name);
         }
    

    【讨论】:

      【解决方案3】:

      您可以创建一个临时列表,并将要删除的 ID 添加到该列表中。然后在循环完成后,删除该列表中的所有项目:

      void timerFunction(object data)
          {
          lock (tMap.SyncRoot)
          {
            IList<UInt32> toDelete = new List<UInt32>();
            foreach (UInt32 id in tMap.Keys)
            {
      
               MyObj obj=(MyObj) runScriptMap[id];
               obj.time = obj.time -1;
               if (obj.time <= 0)
               {                      
                  toDelete.add(id);
               }
             }
            foreach(UInt32 i in toDelete)
            {
                tMap.Remove(i);
            }
          }
      

      【讨论】: