Seltsame „Sammlung wurde modifiziert, nachdem der Aufzähler ausgesetzt war“ Ausnahme

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

  •  20-08-2019
  •  | 
  •  

Frage

Vielleicht kann mich jemand in die richtige Richtung weisen, weil ich darüber völlig ratlos bin.

Ich habe eine Funktion, die einfach eine Linkedlist von Klassen ausdruckt:

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

Das Component Objekt hat tatsächlich einen Brauch ToString() Rufen Sie als solche an:

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

Diese Funktion funktioniert normalerweise einwandfrei - ich bin jedoch auf das Problem gestoßen, dass bei der Aufbau von etwa 30 Einträgen in der Liste die PrintcomplentList foreach Aussage kommt mit einem zurück InvalidOperationException: Collection was modified after the enumerator was instantiated.

Wie Sie sehen, ändere ich den Code nicht in der for -Loop und habe keine expliziten Threads erstellt, obwohl dies in einer XNA -Umgebung liegt (falls es wichtig ist). Es ist zu beachten, dass der Ausdruck häufig genug ist, dass die Konsolenausgabe das Programm insgesamt verlangsamt.

Ich bin völlig ratlos, ist noch jemand da draußen darauf getroffen?

War es hilfreich?

Lösung

Ich vermute, dass der Ort zum Aussehen an allen Stellen sein wird, an denen Sie die Liste manipulieren - dh einfügen/entfernen/neu zuordnen. Mein Verdacht ist, dass es irgendwo einen Rückruf/einen geraden Handler geben wird, der asynchron gefeuert wird (vielleicht als Teil der XNA Farbe usw. Schleifen) und die die Liste bearbeitet - und das Problem im Wesentlichen als Rassenbedingung verursacht.

Um zu überprüfen, ob dies der Fall ist, geben Sie eine Debug-/Trace -Ausgabe an die Orte an, an denen die Liste manipuliert wird, und prüfen Sie, ob sie jemals (und insbesondere kurz vor der Ausnahme) den Manipulationscode gleichzeitig wie Ihre Konsolenausgabe ausführt:

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

Leider sind solche Dinge oft ein Schmerz, um zu debuggen, da das Ändern des Codes, um ihn zu untersuchen, häufig das Problem ändert (a Heisenbug).

Eine Antwort wäre, den Zugriff zu synchronisieren. dh rein alle Die Orte, die die Liste bearbeiten, verwenden a lock rund um den vollständigen Betrieb:

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
}

und in Ihrem Rückruf (oder was auch immer)

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

Insbesondere kann ein "vollständiger Betrieb" enthalten sein:

  • Überprüfen Sie die Anzahl und foreach/for
  • Überprüfen Sie die Existenz und Einlegen/entfernen
  • etc

(dh nicht die individuellen/diskreten Operationen - sondern Einheiten der Arbeit)

Andere Tipps

Anstatt von foreach, Ich benutze while( collection.count >0) dann benutze collection[i].

Ich weiß nicht, ob dies für das OP relevant ist, aber ich hatte den gleichen Fehler und fand diesen Thread während einer Google -Suche. Ich konnte es lösen, indem ich nach dem Entfernen eines Elements in der Schleife eine Pause hinzufügte.

foreach( Weapon activeWeapon in activeWeapons ){

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

Wenn Sie die Pause auslassen, erhalten Sie den Fehler "InvalidOperationException: Die Sammlung wurde geändert, nachdem der Aufzähler instanziiert wurde."

Verwendung Break könnte ein Weg sein, aber es kann sich auf Ihre Betriebserie auswirken. Was ich in diesem Fall in einfach umwandeln, konvertieren Sie einfach das foreach zu traditionell for Schleife

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

Dies funktioniert ohne Probleme.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top