队列ForEach循环抛出InvalidOperationException

问题描述 投票:11回答:6

我之前没有使用过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迭代的集合,但是这个集合是在循环之外执行的吗?

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

谢谢

c# .net silverlight queue invalidoperationexception
6个回答
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();
}

21
投票

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

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

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

干杯


2
投票

老帖但认为我会提供更好的答案:

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

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

如果队列为空,DarkUrse的答案可能会导致异常


1
投票

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

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

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

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


0
投票

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

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

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

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


0
投票

在哪里修改集合并不重要。如果在枚举其成员时修改了集合,则会出现异常。您可以使用锁定并确保在执行时不修改集合,或者如果您使用的是.NET 4.0,则将Queue替换为ConcurrentQueue

© www.soinside.com 2019 - 2024. All rights reserved.