C# 奇怪的“在枚举器实例化后修改了集合”异常
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/844850/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Strange "Collection was modified after the enumerator was instantiated" exception
提问by cyberconte
Perhaps someone can point me in the correct direction, because I'm completely stumped on this.
也许有人可以指出我正确的方向,因为我完全被这个难住了。
I have a function that simply prints out a LinkedList of classes:
我有一个函数可以简单地打印出一个 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("------");
}
The Component
object actually has a custom ToString()
call as such:
该Component
对象实际上有一个自定义ToString()
调用,如下所示:
int Id;
...
public override String ToString()
{
return GetType() + ": " + Id;
}
This function typically works fine - however I've run into the issue that when it builds to about 30 or so entries in the list, the PrintcomplentList
foreach
statement comes back with an InvalidOperationException: Collection was modified after the enumerator was instantiated.
这个函数通常工作正常 - 但是我遇到了一个问题,当它在列表中建立大约 30 个条目时,该PrintcomplentList
foreach
语句返回一个InvalidOperationException: Collection was modified after the enumerator was instantiated.
Now as you can see I'm not modifying the code within the for loop, and I haven't explicitly created any threads, although this is within an XNA environment (if it matters). It should be noted that the printout is frequent enough that the Console output is slowing down the program as a whole.
现在您可以看到,我没有修改 for 循环中的代码,也没有明确创建任何线程,尽管这是在 XNA 环境中(如果重要的话)。应该注意的是,打印输出足够频繁,以至于控制台输出会减慢整个程序的速度。
I'm completely stumped, has anyone else out there run into this?
我完全被难住了,有没有其他人遇到过这种情况?
采纳答案by Marc Gravell
I suspect the place to start looking will be at any places where you manipulate the list - i.e. insert/remove/re-assign items. My suspicion is that there will be a callback/even-handler somewhere that is getting fired asynchronously (perhaps as part of the XNA paintetc loops), and which is editing the list - essentially causing this problem as a race condition.
我怀疑开始查找的位置将是您操作列表的任何位置 - 即插入/删除/重新分配项目。我怀疑某处会有一个回调/偶数处理程序被异步触发(可能作为 XNA绘制等循环的一部分),并且正在编辑列表 - 基本上导致这个问题作为竞争条件。
To check if this is the case, put some debug/trace output around the places that manipulate the list, and see if it ever (and in particular, just before the exception) runs the manipulation code at the same time as your console output:
要检查是否是这种情况,请在操作列表的位置周围放置一些调试/跟踪输出,并查看它是否曾经(特别是在异常之前)与控制台输出同时运行操作代码:
private void SomeCallback()
{
Console.WriteLine("---Adding foo"); // temp investigation code; remove
components.AddLast(foo);
Console.WriteLine("---Added foo"); // temp investigation code; remove
}
Unfortunately, such things are often a pain to debug, as changing the code to investigate it often changes the problem (a Heisenbug).
不幸的是,这样的事情通常很难调试,因为更改代码来调查它通常会改变问题(Heisenbug)。
One answer would be to synchronize access; i.e. in allthe places that edit the list, use a lock
around the complete operation:
一个答案是同步访问;即在所有编辑列表的地方,使用一个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
}
and in your callback (or whatever)
并在您的回调中(或其他)
private void SomeCallback()
{
lock(syncLock)
{
components.AddLast(foo);
}
}
In particular, a "complete operation" might include:
特别是,“完整操作”可能包括:
- check the count and
foreach
/for
- check for existance andinsert/remove
- etc
- 检查计数和
foreach
/for
- 检查是否存在并插入/删除
- 等等
(i.e. not the individual/discrete operations - but units of work)
(即不是个人/离散操作 - 而是工作单元)
回答by arash
Instead of foreach
, I use while( collection.count >0)
then use collection[i]
.
而不是foreach
,我使用while( collection.count >0)
然后使用collection[i]
。
回答by Steffan
I don't know if this is relevant to the OP but I had the same error and found this thread during a google search. I was able to solve it by adding a break after removing an element in the loop.
我不知道这是否与 OP 相关,但我遇到了同样的错误,并在谷歌搜索过程中找到了这个线程。我能够通过在删除循环中的元素后添加中断来解决它。
foreach( Weapon activeWeapon in activeWeapons ){
if (activeWeapon.position.Z < activeWeapon.range)
{
activeWeapons.Remove(activeWeapon);
break; // Fixes error
}
else
{
activeWeapon.position += activeWeapon.velocity;
}
}
}
If you leave out the break, you will get the error "InvalidOperationException: Collection was modified after the enumerator was instantiated."
如果省略中断,您将收到错误“InvalidOperationException:实例化枚举器后修改了集合”。
回答by Saikat Chakraborty
Using Break
could be a way but it may impact your series of operation.
What I do in that case in simply convert the foreach
to traditional for
loop
使用Break
可能是一种方式,但它可能会影响您的一系列操作。在那种情况下我所做的只是将循环转换foreach
为传统for
循环
for(i=0; i < List.count; i++)
{
List.Remove();
i--;
}
This works without any issues.
这没有任何问题。