队列ForEach循环抛出InvalidOperationException

时间:2011-06-04 01:30:40

标签: c# .net silverlight queue invalidoperationexception

我之前没有使用Queues<T>任何真实的学位,所以我可能会遗漏一些明显的东西。我正在尝试迭代这样的Queue<EnemyUserControl>(每一帧):

foreach (var e in qEnemy)
{
     //enemy AI code
}

当敌人死亡时,敌方用户控制会引发我订阅的事件并执行此操作(队列中的第一个敌人被设计删除):

void Enemy_Killed(object sender, EventArgs e)
{      
     qEnemy.Dequeue();

     //Added TrimExcess to check if the error was caused by NULL values in the Queue (it wasn't :))
     qEnemy.TrimExcess();
}

然而,在调用Dequeue方法之后,我在InvalidOperationException循环上得到foreach。当我使用Peek时,没有错误,所以它必须对Queue本身的更改做一些事情,因为Dequeue删除了对象。 我最初的猜测是,它正在抱怨我正在修改一个由Enumerator迭代的集合,但是这个集合是在循环之外执行的吗?

任何可能导致此问题的想法?

由于

6 个答案:

答案 0 :(得分:21)

我知道这是一个老帖子,但以下内容如何:

var queue = new Queue<int>();
queue.Enqueue(1);
queue.Enqueue(2);

while (queue.Count > 0)
{
  var val = queue.Dequeue();
}

干杯

答案 1 :(得分:17)

您正在修改foreach循环内的队列。这是引起例外的原因 用于演示此问题的简化代码:

var queue = new Queue<int>();
queue.Enqueue(1);
queue.Enqueue(2);

foreach (var i in queue)
{
    queue.Dequeue();
}

可能的解决方案是添加ToList(),如下所示:

foreach (var i in queue.ToList())
{
    queue.Dequeue();
}

答案 2 :(得分:1)

这是枚举器的典型行为。大多数枚举器只有在底层集合保持静态时才能正常运行。如果在枚举集合时更改了集合,则MoveNext块为您注入的foreach的下一次调用将生成此异常。

Dequeue操作显然会改变集合,这就是造成问题的原因。解决方法是将要从目标集合中删除的每个项目添加到第二个集合中。循环完成后,您可以循环完成第二个集合并从目标中删除。

然而,这可能有点尴尬,至少,因为Dequeue操作只删除下一个项目。您可能必须切换到允许任意删除的其他集合类型。

如果您想坚持使用Queue,那么您将被迫出列每个项目,并有条件地重新排列那些不应删除的项目。您仍然需要第二个集合来跟踪可以从重新排队中省略的项目。

答案 3 :(得分:1)

旧帖子,但我想我会提供更好的答案:

var queue = new Queue<int>();
queue.Enqueue(1);
queue.Enqueue(2);

while (queue.Any())
{
  var val = queue.Dequeue();
}

由于DarkUrse的回答可能会在队列为空时导致异常

答案 4 :(得分:0)

迭代时,您无法从集合中删除元素。

我找到的最佳解决方案是使用“List&lt;&gt; toDelete”并将要删除的内容添加到该列表中。 foreach循环结束后,您可以使用toDelete列表中的引用从目标集合中删除元素,如下所示:

foreach (var e in toDelete)
    target.Remove(e);
toDelete.Clear();

既然这是一个Queue,你可能只需要计算你想要在一个整数中出队的次数,并使用一个简单的for循环来执行它们(我没有那么多的队列经验)方面)。

答案 5 :(得分:0)

修改集合的位置无关紧要。如果在枚举其成员时修改了集合,则会出现异常。您可以使用锁定并确保在设置时不会修改集合,或者使用.NET 4.0将Queue替换为ConcurrentQueue

相关问题