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

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

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

LinkedList components = new LinkedList(); ... 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语句返回InvalidOperationException: Collection was modified after the enumerator was instantiated.

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

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

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

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

 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 components = new LinkedList(); 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
  • 检查是否存在插入/删除
  • 等等

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

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

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

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

如果省略中断,则会收到错误“InvalidOperationException:在实例化枚举数后修改了集合”。

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

 for(i=0;i 

这没有任何问题。