奇怪的“集合在枚举器实例化后被修改”异常

时间:2009-05-10 06:57:24

标签: c# enumeration

也许有人可以指出我正确的方向,因为我完全被这个困扰了。

我有一个只打印出类的LinkedList的函数:

    LinkedList<Component> components = new LinkedList<Component>();
    ...
    private void PrintComponentList()
    {
        Console.WriteLine("---Component List: " + components.Count + " entries---");
        foreach (Component c in components)
        {
            Console.WriteLine(c);
        }
        Console.WriteLine("------");
    }

Component对象实际上有一个自定义ToString()调用:

    int Id;
    ...
    public override String ToString()
    {
        return GetType() + ": " + Id;
    }

这个函数通常运行正常 - 但是我遇到的问题是,当它在列表中构建大约30个条目时,PrintcomplentList foreach语句返回{{1 }}

现在您可以看到我没有修改for循环中的代码,并且我没有显式创建任何线程,尽管这是在XNA环境中(如果它很重要)。应该注意的是,打印输出频繁,控制台输出整体上减慢了程序的速度。

我完全难过了,还有其他人遇到过这个吗?

4 个答案:

答案 0 :(得分:11)

我怀疑开始寻找的地方会在你操纵列表的任何地方 - 即插入/移除/重新分配项目。我怀疑是会有一个回调/偶数处理程序,它会被异步触发(可能作为XNA paint 等循环的一部分),并且正在编辑列表 - 实际上导致此问题竞争条件。

要检查是否是这种情况,请在操作列表的位置周围放置一些调试/跟踪输出,并查看它是否曾经(特别是在异常之前)与您的同时运行操作代码控制台输出:

private void SomeCallback()
{
   Console.WriteLine("---Adding foo"); // temp investigation code; remove
   components.AddLast(foo);
   Console.WriteLine("---Added foo"); // temp investigation code; remove
}

不幸的是,这些事情通常很难调试,因为更改代码来调查它通常会改变问题(Heisenbug)。

一个答案是同步访问;即在所有编辑列表的地方,在整个操作周围使用lock

LinkedList<Component> components = new LinkedList<Component>();
readonly object syncLock = new object();
...
private void PrintComponentList()
{
    lock(syncLock)
    { // take lock before first use (.Count), covering the foreach
        Console.WriteLine("---Component List: " + components.Count
              + " entries---");
        foreach (Component c in components)
        {
           Console.WriteLine(c);
        }
        Console.WriteLine("------");
    } // release lock
}

和你的回调(或其他)

private void SomeCallback()
{
   lock(syncLock)
   {
       components.AddLast(foo);
   }
}

特别是,“完整操作”可能包括:

  • 检查计数 foreach / for
  • 检查是否存在插入/删除

(即不是个人/离散的操作 - 而是工作单位)

答案 1 :(得分:5)

我使用foreach而不是while( collection.count >0),而是使用collection[i]

答案 2 :(得分:2)

我不知道这是否与OP有关但我有同样的错误,并在谷歌搜索期间找到了这个帖子。我能够通过在删除循环中的元素后添加一个中断来解决它。

foreach( Weapon activeWeapon in activeWeapons ){

            if (activeWeapon.position.Z < activeWeapon.range)
            {
                activeWeapons.Remove(activeWeapon);
                break; // Fixes error
            }
            else
            {
                activeWeapon.position += activeWeapon.velocity;
            }
        }
    }

如果省略休息,您将收到错误“InvalidOperationException:在实例化枚举数后修改了集合。”

答案 3 :(得分:0)

使用Break可能是一种方式,但它可能会影响您的一系列操作。 在这种情况下我只需将foreach转换为传统的for循环

即可
for(i=0; i < List.count; i++)
{
    List.Remove();
    i--;
}

这没有任何问题。