Strange "La colección se modificó después de la instancia de la instancia".

StackOverflow https://stackoverflow.com/questions/844850

  •  20-08-2019
  •  | 
  •  

Pregunta

Quizás alguien pueda señalarme en la dirección correcta, porque estoy completamente perplejo por esto.

Tengo una función que simplemente imprime una lista de clases Linkeds:

    LinkedList<Component> components = new LinkedList<Component>();
    ...
    private void PrintComponentList()
    {
        Console.WriteLine("---Component List: " + components.Count + " entries---");
        foreach (Component c in components)
        {
            Console.WriteLine(c);
        }
        Console.WriteLine("------");
    }

los Component El objeto en realidad tiene una costumbre ToString() Llame como tal:

    int Id;
    ...
    public override String ToString()
    {
        return GetType() + ": " + Id;
    }

Esta función generalmente funciona bien: sin embargo, me he encontrado con el problema de que cuando se acumula en aproximadamente 30 entradas en la lista, la PrintcomplentList foreach La declaración vuelve con un InvalidOperationException: Collection was modified after the enumerator was instantiated.

Ahora, como puede ver, no estoy modificando el código dentro del bucle for, y no he creado explícitamente ningún hilo, aunque esto está dentro de un entorno XNA (si importa). Cabe señalar que la impresión es lo suficientemente frecuente como para que la salida de la consola esté ralentizando el programa en su conjunto.

Estoy completamente perplejo, ¿alguien más se ha encontrado con esto?

¿Fue útil?

Solución

Sospecho que el lugar para comenzar a buscar estará en cualquier lugar donde manipule la lista, es decir, insertar/eliminar/reasignar elementos. Mi sospecha es que habrá una devolución de llamada/manejo uniforme en algún lugar que se dispare asincrónicamente (tal vez como parte de la XNA pintar Etc bucles), y que está editando la lista, esencialmente causando este problema como una condición de carrera.

Para verificar si este es el caso, coloque una salida de depuración/traza en los lugares que manipulan la lista y vea si alguna vez (y en particular, justo antes de la excepción) ejecuta el código de manipulación al mismo tiempo que la salida de su consola:

private void SomeCallback()
{
   Console.WriteLine("---Adding foo"); // temp investigation code; remove
   components.AddLast(foo);
   Console.WriteLine("---Added foo"); // temp investigation code; remove
}

Desafortunadamente, tales cosas son a menudo un dolor para depurar, ya que cambiar el código para investigarlo a menudo cambia el problema (un Casquillo).

Una respuesta sería sincronizar el acceso; es decir todos Los lugares que editan la lista, usan un lock Alrededor de la operación completa:

LinkedList<Component> components = new LinkedList<Component>();
readonly object syncLock = new object();
...
private void PrintComponentList()
{
    lock(syncLock)
    { // take lock before first use (.Count), covering the foreach
        Console.WriteLine("---Component List: " + components.Count
              + " entries---");
        foreach (Component c in components)
        {
           Console.WriteLine(c);
        }
        Console.WriteLine("------");
    } // release lock
}

y en tu devolución de llamada (o lo que sea)

private void SomeCallback()
{
   lock(syncLock)
   {
       components.AddLast(foo);
   }
}

En particular, una "operación completa" podría incluir:

  • Verifique el recuento y foreach/for
  • Verifique la existencia y insertar/quitar
  • etc.

(es decir, no las operaciones individuales/discretas, sino unidades de trabajo)

Otros consejos

En vez de foreach, Yo suelo while( collection.count >0) Luego usa collection[i].

No sé si esto es relevante para el OP, pero tuve el mismo error y encontré este hilo durante una búsqueda en Google. Pude resolverlo agregando un descanso después de eliminar un elemento en el bucle.

foreach( Weapon activeWeapon in activeWeapons ){

            if (activeWeapon.position.Z < activeWeapon.range)
            {
                activeWeapons.Remove(activeWeapon);
                break; // Fixes error
            }
            else
            {
                activeWeapon.position += activeWeapon.velocity;
            }
        }
    }

Si deja el descanso, obtendrá el error "InvalidEperationException: la colección se modificó después de la instancia del enumerador".

Usando Break Podría ser una forma, pero puede afectar su serie de operaciones. Lo que hago en ese caso simplemente convierta el foreach a tradicional for círculo

for(i=0; i < List.count; i++)
{
    List.Remove();
    i--;
}

Esto funciona sin ningún problema.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top