Strana "Collezione è stata modificata dopo che l'enumeratore è stato istanziato" Eccezione

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

  •  20-08-2019
  •  | 
  •  

Domanda

Forse qualcuno può indicarmi nella direzione corretta, perché sono completamente sconcertato su questo.

Ho una funzione che stampela semplicemente una lista di lezioni Linked:

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

Il Component L'oggetto in realtà ha un'usanza ToString() Chiama come tale:

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

Questa funzione in genere funziona bene, tuttavia ho riscontrato il problema che quando si basa su circa 30 voci nell'elenco, il PrintcomplentList foreach la dichiarazione ritorna con un InvalidOperationException: Collection was modified after the enumerator was instantiated.

Ora, come puoi vedere, non sto modificando il codice all'interno del loop per loop e non ho creato esplicitamente alcun thread, sebbene questo sia all'interno di un ambiente XNA (se conta). Va notato che la stampa è abbastanza frequente che l'output della console sta rallentando il programma nel suo insieme.

Sono completamente perplesso, qualcun altro là fuori ha incontrato questo?

È stato utile?

Soluzione

Sospetto che il posto dove iniziare a cercare sarà in tutti i luoghi in cui manipoli l'elenco - cioè inserisci/rimuovi/riassegna gli elementi. Il mio sospetto è che ci sarà un callback/un gestore pari da qualche parte che viene sparato in modo asincrono (forse come parte dell'XNA dipingere ETC LOOP) e che sta modificando l'elenco - causando essenzialmente questo problema come condizione di gara.

Per verificare se questo è il caso, inserisci un output di debug/traccia attorno ai luoghi che manipolano l'elenco e vedi se mai (e in particolare, poco prima dell'eccezione) esegue il codice di manipolazione contemporaneamente all'output della console:

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

Sfortunatamente, tali cose sono spesso un dolore al debug, poiché cambiare il codice per indagare spesso cambia il problema (a Heisenbug).

Una risposta sarebbe quella di sincronizzare l'accesso; cioè in tutto I luoghi che modificano l'elenco, usano un file lock Intorno all'operazione 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
}

E nel tuo callback (o altro)

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

In particolare, un "funzionamento completo" potrebbe includere:

  • Controlla il conteggio e foreach/for
  • controlla l'esistenza e inserire/rimuovere
  • eccetera

(cioè non le operazioni individuali/discrete - ma unità di lavoro)

Altri suggerimenti

Invece di foreach, Io uso while( collection.count >0) Quindi usa collection[i].

Non so se questo sia rilevante per l'OP, ma ho avuto lo stesso errore e ho trovato questo thread durante una ricerca su Google. Sono stato in grado di risolverlo aggiungendo una pausa dopo aver rimosso un elemento nel ciclo.

foreach( Weapon activeWeapon in activeWeapons ){

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

Se lasci fuori la pausa, otterrai l'errore "InvalidOperationException: la raccolta è stata modificata dopo l'istanziata dell'enumeratore."

Using Break could be a way but it may impact your series of operation. What I do in that case in simply convert the foreach to traditional for loop

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

This works without any issues.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top