2014-11-03 7 views
101
var ints = new List<int>(new[ ] { 
    1, 
    2, 
    3, 
    4, 
    5 
}); 
var first = true; 
foreach(var v in ints) { 
    if (first) { 
     for (long i = 0 ; i < int.MaxValue ; ++i) { //<-- The thing I iterate 
      ints.Add(1); 
      ints.RemoveAt(ints.Count - 1); 
     } 
     ints.Add(6); 
     ints.Add(7); 
    } 
    Console.WriteLine(v); 
    first = false; 
} 

Wenn Sie die innere for Schleife auskommentieren, wirft es, es ist offensichtlich, weil wir Änderungen an der Sammlung vorgenommen haben.Warum dieser Code wirft 'Sammlung wurde geändert', aber wenn ich etwas davor iteriere, ist es nicht?

Jetzt, wenn Sie es auskommentieren, warum diese Schleife uns erlauben, diese zwei Elemente hinzuzufügen? Es dauert eine Weile, es wie eine halbe Minute zu laufen (auf Pentium CPU), aber nicht werfen, und das Komische ist, dass es gibt:

Image

Es wurde ein bisschen erwartet, aber es zeigt an, dass wir Änderungen vornehmen können und die Sammlung tatsächlich ändert. Irgendwelche Ideen, warum dieses Verhalten auftritt?

+2

Das ist interessant. Ich könnte das Verhalten reproduzieren, aber nicht, wenn ich die interne Schleife von Int.MaxValue auf einen Wert wie 100 ändern – Steve

+0

Wie lange hast du gewartet? Es dauert eine ganze Weile, um "int.MaxValue" Iterationen zu beenden ... –

+0

@JonSkeet Ich sagte es unter dem Code, eine halbe Minute. (On Pentium CPU) – LyingOnTheSky

Antwort

118

Das Problem ist, dass die Art und Weise, dass List<T> erkennt Änderungen durch ein Versionsfeld, des Typs int, inkrementieren es bei jeder Änderung. Daher, wenn Sie genau einige von 2 Änderungen an der Liste zwischen Iterationen gemacht haben, werden diese Änderungen für die Erkennung unsichtbar machen. (Es wird überlaufen von int.MaxValue zu int.MinValue und schließlich zu seinem ursprünglichen Wert zurück.)

Wenn Sie so ziemlich alles über Ihren Code ändern - fügen Sie 1 oder 3 Werte statt 2, oder verringern Sie die Anzahl der Iterationen Ihrer inneren Schleife um 1, dann wird wie erwartet eine Ausnahme ausgelöst.

(Dies ist ein Implementierungsdetail und kein spezifisches Verhalten - und es ist ein Implementierungsdetail, das in einem sehr seltenen Fall als Fehler betrachtet werden kann. Es wäre jedoch sehr ungewöhnlich, dass es in einem echten Programm ein Problem verursacht .)

+5

Nur als Referenz: [relevanter Quellcode] (http://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs#1224), beachten Sie, dass das Feld '_version' ein' int' ist. –

+1

Yup, es ist genau richtig eingerichtet, so dass _version nach dem Beenden der for-Schleife den Wert -2 hat. Wenn Sie dann 6 und 7 hinzufügen, wird es auf 0 gesetzt, wodurch die Liste unverändert aussieht. – Kaz

+4

Ich bin nicht sicher, dass dies ein "Implementierungsdetail" genannt werden sollte, weil es einen Nebeneffekt dieser Implementierungsentscheidung gibt, der, selbst wenn es unwahrscheinlich ist, real ist.Die Spezifikation (oder zumindest das Dokument) sagt, dass sie eine "InvalidOperationException" auslösen sollte, was eigentlich nicht immer wahr ist. Dies hängt natürlich von der Definition des "Implementierungsdetails" ab. – ken2k

Verwandte Themen