Queue ForEach循环抛出InvalidOperationException

问题描述:

我还没有使用Queues<T>之前的任何真正的程度,所以我可能会失去明显的东西。我试图通过Queue<EnemyUserControl>像这样(每帧)进行迭代:Queue ForEach循环抛出InvalidOperationException

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(); 
} 

然而,出列方法被调用后,我坐上foreach循环的InvalidOperationException。当我使用Peek代替时,没有错误,所以它必须对Queue本身进行更改,因为Dequeue删除了该对象。 我最初的猜测是它抱怨说我正在修改由Enumerator迭代的集合,但是在循环之外执行出列?

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

谢谢

+1

您应该使用 '而(queue.Any())queue.Dequeue();' – Telemat 2015-02-14 09:45:35

您正在修改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(); 
} 
+0

d '哦,是一个facepalm时刻。 AI代码中的一种方法调用'Movement'方法,该方法反过来引发杀死的事件(我认为它是由循环外部的代码引发的),所以在循环内执行出列。 'ToList()'方法完美地工作。谢谢! – keyboardP 2011-06-04 01:46:00

这是普查员的典型行为。大多数统计员只有在基础集合保持静态时才能正确运行。如果集合在列举集合时发生更改,则块将为您注入的MoveNext的下一个调用将生成此异常。

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

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

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

在遍历它们不能从集合中删除元素。

我发现的最佳解决方案是使用“列表<>删除”,并添加任何你想删除的列表。一旦foreach循环结束后,您可以删除使用的toDelete列表中引用的目标集合中的元素,像这样:

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

现在,因为这是一个队列,你可能只是可以算的次数你希望以一个整数出队并使用一个简单的for循环来稍后执行它们(在这方面,我没有那么多队列经验)。

+0

您可以简单地迭代队列并清除它。在这种情况下,效果与使用List相同,所以使用Queue对此没有意义(如果您不需要单独出列)。 – arni 2014-12-11 09:29:48

无论你在哪里修改集合。如果在枚举其成员时修改了集合,则会发生异常。您可以使用锁定,并确保在使用.NET 4.0替换QueueConcurrentQueue时,集合未被修改。

我知道这是一个老的文章,但怎么样以下几点:

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

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

干杯

+4

我建议稍微修改一下这段时间,而不是do/while,以便在尝试第一个.Dequeue()之前执行.Count检查,以防队列为空。 – DaveD 2015-11-04 15:10:18