C# “奇怪”;枚举数实例化后修改了集合";例外

C# “奇怪”;枚举数实例化后修改了集合";例外,c#,enumeration,C#,Enumeration,也许有人能给我指出正确的方向,因为我完全被这个问题难住了 我有一个函数,只需打印一个类的LinkedList: LinkedList<Component> components = new LinkedList<Component>(); ... private void PrintComponentList() { Console.WriteLine("---Component List: " + components.C

也许有人能给我指出正确的方向,因为我完全被这个问题难住了

我有一个函数,只需打印一个类的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("------");
    }
此函数通常工作正常-但是我遇到了一个问题,即当它构建到列表中大约30个条目时,
printCompletList
foreach
语句返回时会出现一个
InvalidOperationException:枚举数实例化后,集合被修改。

现在您可以看到,我没有在for循环中修改代码,也没有显式创建任何线程,尽管这是在XNA环境中(如果有关系的话)。应该注意的是,打印输出的频率足够高,以至于控制台输出会降低整个程序的速度


我完全被难住了,有没有其他人遇到过这种情况?

我怀疑开始寻找的地方将是您操纵列表的任何地方,即插入/删除/重新分配项目。我怀疑在某个地方会有一个回调/偶数处理程序被异步触发(可能是XNA paint etc循环的一部分),它正在编辑列表——这本质上是由于竞争条件导致了这个问题

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

private void SomeCallback()
{
   Console.WriteLine("---Adding foo"); // temp investigation code; remove
   components.AddLast(foo);
   Console.WriteLine("---Added foo"); // temp investigation code; remove
}
不幸的是,这样的事情往往是调试的一个难题,因为更改代码来研究它常常会改变问题(a)

一个答案是同步访问;i、 e.在编辑列表的所有位置,在整个操作周围使用
锁定

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
}
具体而言,“完整操作”可能包括:

  • 检查每个/
    的计数
  • 检查是否存在插入/删除

(即,不是单个/离散操作-而是工作单元)

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

我不知道这是否与OP相关,但我在谷歌搜索过程中发现了相同的错误。在移除循环中的一个元素后,我可以通过添加一个中断来解决这个问题

foreach( Weapon activeWeapon in activeWeapons ){

            if (activeWeapon.position.Z < activeWeapon.range)
            {
                activeWeapons.Remove(activeWeapon);
                break; // Fixes error
            }
            else
            {
                activeWeapon.position += activeWeapon.velocity;
            }
        }
    }
foreach(主动武器中的武器主动武器){
if(主动武器位置Z<主动武器范围)
{
主动武器。移除(主动武器);
break;//修复错误
}
其他的
{
主动武器.位置+=主动武器.速度;
}
}
}

如果省略中断,将出现错误“InvalidOperationException:枚举数实例化后集合被修改。”

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

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

这样做没有任何问题。

听起来确实非常奇怪。你能发布一个简短但完整的程序,这样我们可以试着复制它吗?我没有一个小程序来复制这种行为,因此,我将在那里看到一个LinkedList的线程安全实现,看看它是否捕获了任何东西注意,在编写跟踪行时记录线程id可以帮助您确定是否存在竞争线程,从而确定竞争的可能性。这引入了另一个bug,但是——在第一个超范围武器之后,列表中的任何武器都不会修改其位置!我在过去处理过这个问题,创建了第二个要从第一个列表中删除的内容列表,将其填充到一个foreach中,然后对第二个列表执行foreach并从第一个列表中删除其中的每个项目。这解决了我的问题。这可能会起作用,但在这种情况下,迭代是在集合的对象上进行的。foreach(collection.keys中的var obj)。。。
foreach( Weapon activeWeapon in activeWeapons ){

            if (activeWeapon.position.Z < activeWeapon.range)
            {
                activeWeapons.Remove(activeWeapon);
                break; // Fixes error
            }
            else
            {
                activeWeapon.position += activeWeapon.velocity;
            }
        }
    }
for(i=0; i < List.count; i++)
{
    List.Remove();
    i--;
}