2016-01-29 17 views
8

diese Frage Nach Foreach loop for disposing controls skipping iterations es nervte mich, dass Iteration über eine sich verändernde Sammlung erlaubt wurde:Warum wirft ControlCollection keine InvalidOperationException?

Zum Beispiel die folgenden:

List<Control> items = new List<Control> 
{ 
    new TextBox {Text = "A", Top = 10}, 
    new TextBox {Text = "B", Top = 20}, 
    new TextBox {Text = "C", Top = 30}, 
    new TextBox {Text = "D", Top = 40}, 
}; 

foreach (var item in items) 
{ 
    items.Remove(item); 
} 

wirft

InvalidOperationException: Die Auflistung wurde geändert; Aufzählungsoperation wird möglicherweise nicht ausgeführt.

jedoch in einer .NET-Formular können Sie tun:

this.Controls.Add(new TextBox {Text = "A", Top = 10}); 
this.Controls.Add(new TextBox {Text = "B", Top = 30}); 
this.Controls.Add(new TextBox {Text = "C", Top = 50}); 
this.Controls.Add(new TextBox {Text = "D", Top = 70}); 

foreach (Control control in this.Controls) 
{ 
    control.Dispose(); 
} 

, welche Elemente überspringt, weil das der Iterator über eine sich verändernde Sammlung läuft, ohne eine Ausnahme zu werfen

Bug? sind nicht Iteratoren erforderlich, um InvalidOperationException zu werfen, wenn sich die darunterliegende Sammlung ändert?

Also meine Frage ist Warum löst Iteration über eine Änderung ControlCollection NICHT InvalidOperationException?

Nachtrag:

Die Dokumentation für IEnumerator sagt:

Der enumerator keinen exklusiven Zugriff hat auf die Sammlung; Daher ist das Aufzählen durch eine Sammlung an sich kein Thread-sicheres Verfahren. Selbst wenn eine Auflistung synchronisiert wird, können andere Threads die Auflistung weiterhin ändern, die verursacht, den Enumerator eine Ausnahme zu werfen.

https://msdn.microsoft.com/en-us/library/system.collections.ienumerator%28v=vs.100%29.aspx

+0

ändert sich 'this.Controls.Count' nachdem die' foreach' abgeschlossen ist? Ich denke, die controllCollection selbst ändert sich nicht. –

+0

@AmitKumarGhosh Ja, da die ControlsCollection den Index der verbleibenden Steuerelemente nach jedem Aufruf von 'Control.ControlCollection.Remove()' neu anordnet – Marco

+0

Nicht alle Auflistungen implementieren dieses Feature. –

Antwort

5

Die Antwort auf diese kann in the Reference Source for ControlCollectionEnumerator

private class ControlCollectionEnumerator : IEnumerator { 
    private ControlCollection controls; 
    private int current; 
    private int originalCount; 

    public ControlCollectionEnumerator(ControlCollection controls) { 
     this.controls = controls; 
     this.originalCount = controls.Count; 
     current = -1; 
    } 

    public bool MoveNext() { 
     // VSWhidbey 448276 
     // We have to use Controls.Count here because someone could have deleted 
     // an item from the array. 
     // 
     // this can happen if someone does: 
     //  foreach (Control c in Controls) { c.Dispose(); } 
     // 
     // We also dont want to iterate past the original size of the collection 
     // 
     // this can happen if someone does 
     //  foreach (Control c in Controls) { c.Controls.Add(new Label()); } 

     if (current < controls.Count - 1 && current < originalCount - 1) { 
      current++; 
      return true; 
     } 
     else { 
      return false; 
     } 
    } 

    public void Reset() { 
     current = -1; 
    } 

    public object Current { 
     get { 
      if (current == -1) { 
       return null; 
      } 
      else { 
       return controls[current]; 
      } 
     } 
    } 
} 

Achten Sie besonders auf die Ausführungen im MoveNext(), die explizit dem abzuhelfen finden.

IMO Dies ist ein fehlgeleitetes "Fix", weil es einen offensichtlichen Fehler maskiert, indem es einen subtilen einführt (Elemente werden still übersprungen, wie vom OP bemerkt).

+0

+1 So erklärt, wie es es vermeidet. Aber sollte es da nicht InvalidOperationException werfen? Die Dokumentation würde bedeuten, dass IEnumerator dies tun sollte. Wenn diese Implikation richtig ist, dann ist es ein Fehler, oder? Aber angesichts der Kommentare im Quellcode, es explizit zu vermeiden. Sehr komisch. –

+0

@MeirionHughes Ich stimme zu - es sieht so aus, als hätten sie versucht, ein wahrgenommenes Problem zu beheben, und dadurch die Dinge noch schlimmer gemacht (wenn Sie es für schlimmer halten, statt eines lauten Fehlers einen stillen Bug zu haben), –

+0

Bis jemand von ms -team erklärt, WARUM sie explizit 'InvalidOperationException' nicht werfen wollten, dies wird die akzeptierte Antwort sein. :) –

Verwandte Themen