Frage

Wir kennen alle wandelbar structs sind Übel im allgemeinen. Ich bin auch ziemlich sicher, dass da IEnumerable<T>.GetEnumerator() kehrt Typ IEnumerator<T> werden die structs sofort in einen Referenztyp geboxt, kostet mehr, als wenn sie einfach Referenztypen waren mit zu beginnen.

Also, warum in den BCL generischen Sammlungen, sind alle Aufzählungen wandelbar structs? Sicherlich hatte es ein guter Grund gewesen sein. Das einzige, was mir einfällt, ist, dass structs leicht kopiert werden kann, wodurch der enumerator Zustand an einem beliebigen Punkt zu erhalten. Aber das Hinzufügen einer Copy() Methode zum IEnumerator Schnittstelle weniger lästig gewesen wäre, so sehe ich das nicht als eine logische Rechtfertigung für sich allein zu sein.

Auch wenn ich mit einer Design-Entscheidung nicht einverstanden sein, ich möchte in der Lage sein, die Gründe zu verstehen.

War es hilfreich?

Lösung

Tatsächlich ist es aus Performance-Gründen. Das BCL-Team hat ein Los die Forschung in diesem Punkt vor zu gehen mit der Entscheidung, was Sie rufen zu Recht als verdächtig und gefährliche Praxis. Die Verwendung eines wandelbaren Werttypen

Sie fragen, warum diese Boxen nicht verursacht. Es ist, weil der C # -Compiler nicht Code-Box Sachen IEnumerable oder IEnumerator in einer foreach-Schleife generiert, wenn er es vermeiden kann!

Wenn wir sehen,

foreach(X x in c)

das erste, was wir tun, ist zu überprüfen, ob c eine Methode namens GetEnumerator hat. Ist dies der Fall, dann überprüfen wir, ob der Typ es gibt Methode Movenext und Eigentum Strom hat. Ist dies der Fall, dann wird die foreach-Schleife erzeugt ganz direkte Anrufe auf diese Methoden und Eigenschaften verwenden. Nur wenn „das Muster“ nicht zugeordnet werden kann tun, greifen wir für die Schnittstellen zu suchen.

Das hat zwei wünschenswerte Effekte.

Erstens, wenn die Sammlung, sagen wir, eine Sammlung von ints, aber wurde geschrieben, bevor generische Typen erfunden wurden, dann ist es nicht die Box-Strafe nehmen den Wert des Stroms zu Objekt des Boxens und Unboxing es dann zu int. Wenn Strom ist eine Eigenschaft, dass die Renditen ein int, wir es einfach verwenden.

Zweitens, wenn der Enumerator ein Werttyp ist dann ist es nicht den Enumerator auf IEnumerator Feld.

Wie ich schon sagte, hat das BCL-Team eine Menge Forschung auf diesem und entdeckte, dass die überwiegende Mehrheit der Zeit, die Strafe der Zuteilung von und Aufheben der Zuordnung der enumerator wurde so groß, dass es das Geld wert machen es ist ein Werttyp, auch wenn so tun kann, einige verrückte Bugs führen.

Betrachten wir zum Beispiel diese:

struct MyHandle : IDisposable { ... }
...
using (MyHandle h = whatever)
{
    h = somethingElse;
}

Sie würden recht erwarten zu Recht, den Versuch zu mutieren h zum Scheitern verurteilt, und in der Tat tut es. Der Compiler erkennt, dass Sie versuchen, den Wert von etwas zu ändern, die eine anhängige Verfügung hat, und dass damit könnte das Objekt führen, dass der Bedarf tatsächlich entsorgt werden nicht entsorgt werden.

Nehmen wir nun an Sie hatte:

struct MyHandle : IDisposable { ... }
...
using (MyHandle h = whatever)
{
    h.Mutate();
}

Was passiert hier? Sie erwarten, vernünftigerweise, dass der Compiler tun würde, was er tut, wenn h ein Nur-Lese-Bereich ist:

Andere Tipps

Struct Methoden inlined werden, wenn Art der Struktur zum Zeitpunkt der Kompilierung bekannt ist, und Aufruf der Methode über die Schnittstelle ist langsam, so Antwort lautet:. Wegen Leistungs Grund

scroll top