c# - Queue ForEach loop throw InvalidOperationException
.net silverlight (5)
Está modificando la cola dentro del ciclo foreach
. Esto es lo que causa la excepción.
Código simplificado para demostrar el problema:
var queue = new Queue<int>();
queue.Enqueue(1);
queue.Enqueue(2);
foreach (var i in queue)
{
queue.Dequeue();
}
La posible solución es agregar ToList()
, así:
foreach (var i in queue.ToList())
{
queue.Dequeue();
}
No he utilizado Queues<T>
en ningún grado real, por lo que podría estar perdiendo algo obvio. Estoy intentando iterar a través de Queue<EnemyUserControl>
como este (cada fotograma):
foreach (var e in qEnemy)
{
//enemy AI code
}
Cuando un enemigo muere, el control de usuario enemigo plantea un evento al que me he suscrito y lo hago (el primer enemigo en la cola se elimina por diseño):
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();
}
Sin embargo, después de que se llama al método Dequeue, obtengo una InvalidOperationException
en el ciclo foreach
. Cuando uso Peek
lugar, no hay errores, por lo que tiene que hacer algo con el cambio de la cola en sí, ya que Dequeue elimina el objeto. Mi suposición inicial es que se está quejando de que estoy modificando una colección que está siendo iterada por el Enumerator, ¿pero el dequeuing se está realizando fuera del bucle?
¿Alguna idea de lo que podría estar causando este problema?
Gracias
Este es el comportamiento típico de los enumeradores. La mayoría de los enumeradores están diseñados para funcionar correctamente solo si la colección subyacente permanece estática. Si la colección se cambia al enumerar una colección, la siguiente llamada a MoveNext
, que el bloque foreach
le inyectará, generará esta excepción.
La operación Dequeue
obviamente cambia la colección y eso es lo que está causando el problema. La solución consiste en agregar cada elemento que desee eliminar de la colección de destino a una segunda colección. Después de completar el ciclo, puede recorrer la segunda colección y eliminarla del objetivo.
Sin embargo, esto podría ser un poco incómodo, al menos, ya que la operación Dequeue
solo elimina el siguiente elemento. Es posible que deba cambiar a un tipo de colección diferente que permita eliminaciones arbitrarias.
Si desea quedarse con una Queue
, se verá obligado a quitar la cola de cada elemento y volver a poner en cola de manera condicional esos elementos que no deberían eliminarse. Aún necesitará la segunda colección para realizar un seguimiento de los elementos que está bien omitir en la re-puesta en cola.
No importa dónde estés modificando la colección. Si se modifica una colección mientras enumera sus miembros, obtiene una excepción. Puede usar bloqueos y asegurarse de que la colección no se modifique al realizar iteraciones o si está utilizando .NET 4.0. Reemplace Queue
con ConcurrentQueue
.
No puede eliminar elementos de una colección mientras itera sobre ellos.
La mejor solución que he encontrado es usar una "Lista <> para Eliminar" y agregar lo que quieras eliminar a esa lista. Una vez que finaliza el ciclo foreach, puede eliminar los elementos de la colección objetivo utilizando las referencias en la lista toDelete como esta:
foreach (var e in toDelete)
target.Remove(e);
toDelete.Clear();
Ahora que se trata de una cola, puede simplemente contar la cantidad de veces que desea Dequeue en un número entero y usar un ciclo simple para ejecutarlas más adelante (no tengo mucha experiencia con las colas en este sentido).
Sé que esta es una publicación anterior, pero ¿qué pasa con lo siguiente?
var queue = new Queue<int>();
queue.Enqueue(1);
queue.Enqueue(2);
do {
var val = queue.Dequeue();
}
while (queue.Count > 0);
Aclamaciones