29 Stimmen

Warum kann man die Werte eines Wörterbuchs nicht ändern, während man seine Schlüssel aufzählt?

class Program
{
    static void Main(string[] args)
    {
        var dictionary = new Dictionary<string, int>()
        {
            {"1", 1}, {"2", 2}, {"3", 3}
        };

        foreach (var s in dictionary.Keys)
        {
            // Throws the "Collection was modified exception..." on the next iteration
            // What's up with that?

            dictionary[s] = 1;  
        }
    }
}

Ich verstehe vollkommen, warum diese Ausnahme beim Aufzählen einer Liste ausgelöst wird. Es scheint vernünftig zu erwarten, dass sich während der Aufzählung die Struktur des aufgezählten Objekts nicht ändert. Ändert sich jedoch eine Wert eines Wörterbuchs ändert auch dessen Struktur ? Insbesondere die Struktur der Schlüssel?

1voto

TheZenker Punkte 1700

Aus der Dokumentation (Dictionary.Item Property):

Sie können auch die Eigenschaft Item verwenden, um neue Elemente hinzuzufügen, indem Sie den Wert eines Schlüssels festlegen, der im Dictionary nicht vorhanden ist. Wenn Sie den Wert der Eigenschaft festlegen, wird der mit dem Schlüssel verbundene Wert durch den zugewiesenen Wert ersetzt, wenn der Schlüssel im Dictionary enthalten ist. Wenn der Schlüssel nicht im Dictionary vorhanden ist, werden Schlüssel und Wert dem Dictionary hinzugefügt. Im Gegensatz dazu ändert die Add-Methode keine bestehenden Elemente.

Wie John andeutet, gibt es für das Framework keine Möglichkeit zu wissen, dass Sie den Inhalt der Liste nicht geändert haben, so dass es davon ausgeht, dass Sie dies getan haben.

3 Stimmen

Ich bin anderer Meinung. Das Framework weiß, dass Sie auf die Indexer-Eigenschaft zugegriffen haben. Es ist trivial zu prüfen, ob die Struktur der Schlüssel ungültig gemacht wurde, und nur in diesem Fall eine Ausnahme ausgeben.

0 Stimmen

@Vitaliy Es ist nicht nur trivial, .net muss es für die Add Methode und in der Tat das tut es bereits .

1voto

Simon Tewsi Punkte 15527

Für diejenigen, die daran interessiert sind, wie man dieses Problem umgehen kann, gibt es hier eine modifizierte Version von Vitaliy's Code, die funktioniert:

class Program
{
    static void Main(string[] args)
    {
        var dictionary = new Dictionary<string, int>()
        {
            {"1", 1}, {"2", 2}, {"3", 3}
        };

        string[] keyArray = new string[dictionary.Keys.Count];
        dictionary.Keys.CopyTo(keyArray, 0);
        foreach (var s in keyArray)
        {
            dictionary[s] = 1;
        }
    }
}

Die Antwort ist, die Schlüssel in ein anderes enumerable zu kopieren und dann über diese Sammlung zu iterieren. Aus irgendeinem Grund gibt es keine KeyCollection.ToList-Methode, die die Sache vereinfacht. Stattdessen müssen Sie die Methode KeyCollection.CopyTo verwenden, die die Schlüssel in ein Array kopiert.

0 Stimmen

Damit ist das Problem nicht gelöst, Partner. Sie erhalten immer noch die Fehlermeldung Collection Modified. Netter Versuch, aber keine Zigarre.

0 Stimmen

@ObiWan: Kein Fehler für mich in einer .NET 4.5 Konsolenanwendung, die in VS 2013 geschrieben wurde. Welche Version von .NET verwenden Sie?

1 Stimmen

Zu der Zeit, als ich 4.6.1 verwendete, habe ich wahrscheinlich nicht genau das getan, was Sie getan haben. Ich entschuldige mich für meine zynische Bemerkung, die ich gemacht habe, weil ich frustriert war. Es erscheint mir lächerlich, dass ich nicht einfach die Schlüsselsammlung durchlaufen und die Werte ändern kann, solange ich die Schlüsselsammlung nicht ändere. Dieses ganze Problem ist IMO auf das schlechte Design der Dictionary-Klasse zurückzuführen, die dies nicht zulässt.

0voto

David R Tribble Punkte 11254

Die kurze Antwort ist, dass Sie die Wörterbuchsammlung ändern, auch wenn Sie eigentlich keinen der Schlüssel ändern. Die nächste Iteration, die nach der Aktualisierung auf die Sammlung zugreift, löst also eine Ausnahme aus, die anzeigt, dass die Sammlung seit dem letzten Zugriff geändert wurde (und das zu Recht).

Um das zu erreichen, was Sie wollen, brauchen Sie eine andere Art der Iteration durch die Elemente, so dass eine Änderung nicht zu einer Iterator-Ausnahme führen wird.

0voto

ed vergara Punkte 1

Das liegt daran, dass .Net mit der Fähigkeit entwickelt wurde, eine Sammlung in mehreren Threads zu iterieren. Also muss man entweder zulassen, dass der Iterator multithreaded ist, oder das verhindern und die Änderung der Sammlung während der Iteration zulassen, was eine Beschränkung des Objekts auf die Iteration in einem einzigen Thread erfordern würde. Man kann nicht beides haben.

In der Tat ist die Antwort auf Ihre Frage, dass der Code, den Sie in tatsächlich Ergebnisse in einem Compiler generiert ([CompilerGenerated]) Zustandsmaschine, die für Iteratoren den Zustand der Sammlung zu halten, um die Ausbeute Magie bereitstellen können. Wenn Sie also Ihre Sammlungen nicht synchronisieren und in einem Thread iterieren und in einem anderen Thread manipulieren, werden Sie einige Probleme bekommen.

Sehen Sie sich das an: http://csharpindepth.com/articles/chapter6/iteratorblockimplementation.aspx

Auch: http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentHashMap.html "Iteratoren sind so konzipiert, dass sie jeweils nur von einem Thread verwendet werden."

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X