Ich frage mich, warum sich C # mehr auf Muster-basierte Programmierung als auf konventionelle Art und Weise bewegt.
Bsp. Die Anweisung foreach
erwartet, dass die Schleifenquelle über eine magische Methode namens GetEnumerator
verfügt, die ein Objekt zurückgibt, das einige weitere magische Methoden wie MoveNext
und Current
enthält, aber keine spezifische Schnittstelle vorgibt. C # könnte die Anweisung haben, dass eine in foreach
zu verwendende Klasse IEnumerable
oder IEnumerable<T>
wie für die using
-Anweisung implementieren soll, da sie erwartet, dass ein Objekt in der using
-Anweisung verwendet wird, um% zu implementieren. co_de% interface.
Ich sehe auch einen ähnlichen Trend mit IDisposable
/ async
-Schlüsselwörtern ....
Natürlich muss es einen guten Grund dafür geben, aber es scheint ein wenig seltsam für mich zu verstehen, warum Compiler / CLR "magische Methoden" erfordert, anstatt auf Schnittstellen angewiesen zu sein.
foreach
Ich würde sagen, es geht sowohl um Leistung als auch um Kompatibilität
foreach
gewählt hätten, um IEnumerable
zu verwenden, hätten Sie alle generisch gemacht
Collections Iteration sehr langsam für Werttypen T
(wegen
Boxen / Unboxing). IEnumerable<T>
iterieren über ArrayList
und verwendet hätten
Alle nicht-generischen Sammlungen aus der frühen .NET-Version wären nicht
möglich. Ich denke, die Designentscheidung war gut. Wenn foreach
eingeführt wurde (.NET 1.1), gab es in .NET nichts mit Generics (sie wurden in .NET 2.0 eingeführt). Wenn Sie IEnumerable
als Quelle für foreach
enumeration wählen, würde dies die Verwendung von generischen Sammlungen mindern oder eine radikale Änderung erfordern. Ich nehme an, Designer wussten bereits, dass sie nicht allzu lange später Generika einführen würden.
Zusätzlich deklarieren Sie es als Verwenden Sie IEnumerable<T>
, wenn es verfügbar ist, oder IEnumerable
, wenn es nicht ist nicht viel anders als verwenden Sie die verfügbare GetEnumerator
-Methode oder kompilieren Sie sie nicht, wenn dies nicht der Fall ist verfügbar , oder?
aktualisieren
Wie @mikez in den Kommentaren erwähnt hat, gibt es einen weiteren Vorteil. Wenn Sie nicht erwarten, dass GetEnumerator
IEnumerator
/ IEnumerator<T>
zurückgibt, können Sie struct
zurückgeben und sich nicht um das Boxen kümmern, wenn der Enumerator von loop verwendet wird.
LINQ
Die gleiche magische Methode -Situation tritt auf, wenn Sie LINQ- und Syntax-basierte Abfragen verwenden. Wenn Sie
schreiben %Vor%es wird vom Compiler in
umgewandelt %Vor% Und weil dieser Code funktionieren würde, egal welche Schnittstelle source
implementiert, gilt das Gleiche für syntaxbasierte Abfragen. Solange nach der Umwandlung in eine methodenbasierte Abfrage kann jeder Methodenaufruf vom Compiler korrekt zugewiesen werden, alles ist in Ordnung.
asynchron / warten
Ich bin mir nicht so sicher, denke aber, dass das gleiche für async
/ await
gilt. Wenn Sie diese Schlüsselwörter verwenden, generiert der Compiler eine Menge Code für sich selbst, der dann so kompiliert wird, als hätten Sie den Code selbst geschrieben. Und solange der von dieser Transformation erzeugte Code kompiliert werden kann, ist alles in Ordnung.