Warteschlange ForEach loop werfend InvalidOperationException

8

Ich habe Queues<T> noch nie zuvor benutzt, also könnte mir etwas offensichtlich fehlen. Ich versuche, ein Queue<EnemyUserControl> wie folgt (jedes Bild) zu durchlaufen:

%Vor%

Wenn ein Gegner stirbt, löst die gegnerische Benutzerkontrolle ein Ereignis aus, das ich abonniert habe, und das mache ich (der erste Feind in der Warteschlange wird vom Design entfernt):

%Vor%

Nach dem Aufruf der Dequeue-Methode erhalte ich jedoch InvalidOperationException in der foreach -Schleife. Wenn ich stattdessen Peek verwende, gibt es keine Fehler, daher muss etwas mit dem Ändern der Warteschlange selbst durchgeführt werden, da die Warteschlange das Objekt entfernt. Meine erste Vermutung ist, dass es beschwert, dass ich eine Sammlung ändere, die von der Enumerator iteriert wird, aber die Auslagerung wird außerhalb der Schleife durchgeführt?

Irgendwelche Ideen, was könnte dieses Problem verursachen?

Danke

    
keyboardP 04.06.2011, 01:30
quelle

5 Antworten

17

Sie ändern die Warteschlange innerhalb von foreach loop. Dies verursacht die Ausnahme.
Vereinfachter Code zur Veranschaulichung des Problems:

%Vor%

Mögliche Lösung ist das Hinzufügen von ToList() , wie folgt:

%Vor%     
Alex Aza 04.06.2011, 01:37
quelle
15

Ich weiß, das ist ein alter Beitrag, aber was ist mit dem folgenden:

%Vor%

Prost

    
DarkUrse 25.11.2013 12:29
quelle
1

Dies ist ein typisches Verhalten von Enumeratoren. Die meisten Enumeratoren funktionieren nur dann ordnungsgemäß, wenn die zugrunde liegende Auflistung statisch bleibt. Wenn die Auflistung während der Auflistung einer Auflistung geändert wird, wird der nächste Aufruf von MoveNext , der vom Block foreach für Sie eingefügt wird, diese Ausnahme generieren.

Die Operation Dequeue ändert offensichtlich die Sammlung, und genau das verursacht das Problem. Die Problemumgehung besteht darin, jedes Element, das Sie aus der Zielsammlung entfernen möchten, in eine zweite Sammlung hinzuzufügen. Nachdem die Schleife abgeschlossen ist, können Sie die zweite Sammlung durchlaufen und aus dem Ziel entfernen.

Dies könnte jedoch zumindest etwas peinlich sein, da die Operation Dequeue nur das nächste Element entfernt. Sie müssen möglicherweise zu einem anderen Sammlertyp wechseln, der beliebige Entfernungen ermöglicht.

Wenn Sie bei Queue bleiben möchten, müssen Sie jedes Element aus der Warteschlange entfernen und die Elemente, die nicht entfernt werden sollen, erneut in die Warteschlange stellen. Sie benötigen weiterhin die zweite Sammlung, um die Elemente zu verfolgen, die beim erneuten Anordnen ausgelassen werden sollen.

    
Brian Gideon 04.06.2011 01:37
quelle
0

Sie können Elemente nicht aus einer Sammlung entfernen, während Sie darüber iterieren.

Die beste Lösung, die ich gefunden habe, besteht darin, eine "Liste & lt; & gt; toDelete" zu verwenden und alles hinzuzufügen, was Sie zu dieser Liste entfernen möchten. Sobald die foreach-Schleife beendet ist, können Sie die Elemente aus der Zielsammlung mithilfe der Referenzen in der toDelete-Liste wie folgt entfernen:

%Vor%

Da es sich hier um eine Warteschlange handelt, können Sie einfach die Anzahl der Auszüge in einer Ganzzahl zählen und eine einfache for-Schleife verwenden, um sie später auszuführen (ich habe nicht so viel Erfahrung mit Warteschlangen in dieser Rücksicht).

    
June Rhodes 04.06.2011 01:39
quelle
0

Es spielt keine Rolle, wo Sie die Sammlung ändern. Wenn eine Auflistung geändert wird, während Sie ihre Mitglieder aufzählen, erhalten Sie eine Ausnahme. Sie können Sperren verwenden und sicherstellen, dass die Auflistung beim Generieren nicht geändert wird, oder wenn Sie .NET 4.0 verwenden, ersetzen Sie Queue durch ConcurrentQueue .

    
Xaqron 04.06.2011 01:40
quelle