2016-01-13 2 views
10

Die compiler-generated implementation von IEnumerator/IEnumerable für yield Methoden und Getter scheint eine Klasse zu sein, und deshalb auf dem Heap zugeordnet wird. Andere .NET-Typen wie List<T> geben jedoch speziell struct Enumeratoren zurück, um eine nutzlose Speicherzuordnung zu vermeiden. Von einem schnellen Überblick über den Beitrag sehe ich keinen Grund, warum das nicht auch hier der Fall sein könnte.Warum ist der vom Compiler generierte Enumerator für "yield" keine Struktur?

Fehle ich etwas?

+0

Ich habe gerade festgestellt, dass, da der Rückgabetyp eine Schnittstelle (IEnumerable oder IEnumerator) ist, sie [http://stackoverflow.com/questions/3032750/] trotzdem gerahmt wird, ist das richtig? In diesem Fall könnte nicht die Methode geändert werden, um eine explizit eingegeben enumerator zurückzukehren (wie [List tut] (https://msdn.microsoft.com/en-us/library/b0yss765 (v = vs.110). aspx))? Da es die Schnittstellen implementiert, sollten alle Codereferenzen zu ihm beibehalten werden. (IIRC, dies funktioniert, weil 'foreach' wird [durch Muster erkannt] (https://blogs.msdn.microsoft.com/ericlippert/2011/06/30/following-the-pattern/)). – Lazlo

Antwort

8

Servy richtig beantwortet Ihre Frage - eine Frage, die Sie sich in einem Kommentar geantwortet:

Ich habe erkannt, dass, da der Rückgabetyp eine Schnittstelle ist, wäre es ohnehin verpackt bekommen, ist das richtig?

Richtig. Ihre folgende Frage ist:

konnte die Methode nicht geändert werden, um einen explizit typisierten Enumerator zurückzugeben (wie List<T> tut)?

Also hier Ihre Idee ist, dass der Benutzer schreibt:

IEnumerable<int> Blah() ... 

und der Compiler generiert tatsächlich eine Methode, die BlahEnumerable zurückgibt, die eine Struktur ist, die IEnumerable<int> implementiert, aber mit der entsprechenden GetEnumerator etc Methoden und Eigenschaften das erlaubt die "pattern matching" -Funktion von foreach, um das Boxen zu elide.

Obwohl das eine plausible Idee ist, gibt es ernsthafte Schwierigkeiten, wenn Sie anfangen, über den Rückgabetyp einer Methode zu lügen. Insbesondere wenn es sich um eine Änderung handelt, ob die Methode eine Struktur oder einen Referenztyp zurückgibt. Denken Sie an all die Dinge, die schief gehen:

  • Angenommen, die Methode virtuell ist. Wie kann es außer Kraft gesetzt werden? Der Rückgabetyp einer virtuellen Überschreibungsmethode muss genau mit der überschriebenen Methode übereinstimmen. (Und ähnlich für: Die Methode überschreibt eine andere Methode, die Methode implementiert eine Methode einer Schnittstelle und so weiter.)

  • Angenommen, die Methode wird in einen Delegaten Func<IEnumerable<int>> gemacht. Func<T> ist kovariant in T, aber Kovarianz gilt nur für Typargumente des Referenztyps.Der Code sieht aus wie es ein IEnumerable<T> aber in der Tat gibt es gibt einen Wert, der nicht Kovarianz-kompatibel mit IEnumerable<T> ist, nur Zuordnung kompatibel.

  • Angenommen, wir haben void M<T>(T t) where T : class und wir rufen M(Blah()). Wir gehen davon aus, abzuleiten, dass T ist IEnumerable<int>, die die Bedingungsprüfung passiert, aber der Strukturtyp hat nicht die Einschränkung Prüfung bestehen.

Und so weiter. Du landest schnell in einer Episode von Three's Company (Junge, mit der ich mich hier treffe), wo eine kleine Lüge sich zu einem großen Desaster zusammenfügt. All dies, um eine kleine Menge Sammeldruck zu sparen. Ist es nicht wert.

Ich bemerke jedoch, dass die durch den Compiler geschaffene Implementierung in einer interessanten Weise Sammeldruck spart. Das ersten Mal, dass GetEnumerator auf dem zurück enumerable genannt wird, schaltet sich die zählbaren selbst in einen Enumerator. Beim zweiten Mal ist der Zustand natürlich anders, also weist er ein neues Objekt zu. Da das zu 99,99% wahrscheinliche Szenario ist, dass eine gegebene Sequenz genau einmal aufgelistet wird, ist dies eine große Ersparnis beim Sammeldruck.

+0

Danke für die gründliche Antwort! Ich bin pingelig auf Sammlung Druck, weil ich mit Unity schrecklich früh Mono Runtime nicht-Generationen-GC gerade arbeite, also bin ich bei Zuweisungen suchen * sehr * eng. – Lazlo

6

Diese Klasse wird nur jemals über die Schnittstelle verwendet werden. Wenn es eine Struktur wäre, würde es 100% der Zeit eingerahmt werden, was es effizienter macht, als eine Klasse zu verwenden.

Sie können nicht nicht Box es, wie es ist, per definitionem unmöglich, die Art zum Zeitpunkt der Kompilierung als zu verwenden, um es nicht existiert, wenn Sie den Code starten kompilieren.

Wenn Sie eine benutzerdefinierte Implementierung von IEnumerator schreiben, können Sie den eigentlichen zugrunde liegenden Typ vor dem Kompilieren des Codes verfügbar machen, so dass dieser möglicherweise ohne eingerahmt verwendet werden kann.

Verwandte Themen