17

我以前没有使用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改为使用时,没有错误,因此它必须对队列本身的更改做一些事情,因为 Dequeue 删除了对象。我最初的猜测是它抱怨我正在修改一个由枚举器迭代的集合,但是出队是在循环之外执行的?

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

谢谢

4

6 回答 6

31

我知道这是一篇旧帖子,但以下内容呢:

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

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

干杯

于 2013-11-25T12:29:47.963 回答
21

您正在修改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();
}
于 2011-06-04T01:37:51.233 回答
5

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

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


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

由于 DarkUrse 的原始答案使用了 do/while,如果尝试在空队列上出队时队列为空,则会导致异常,还添加了针对空队列的保护

于 2018-07-10T11:52:43.700 回答
1

这是枚举器的典型行为。大多数枚举器被设计为仅在基础集合保持静态时才能正常工作。如果在枚举集合时更改了集合MoveNext,则块为您注入的下一次调用foreach将生成此异常。

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

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

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

于 2011-06-04T01:37:50.313 回答
0

您不能在迭代集合时从集合中移除元素。

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

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

现在,由于这是一个队列,您可以简单地计算您想要以整数出列的次数,并使用简单的 for 循环稍后执行它们(我在这方面对队列没有太多经验)。

于 2011-06-04T01:39:40.187 回答
0

在哪里修改集合并不重要。如果在枚举其成员时修改了集合,则会出现异常。您可以使用锁并确保在迭代时不修改集合,或者如果您使用 .NET 4.0 替换QueueConcurrentQueue.

于 2011-06-04T01:40:31.403 回答