2009-11-26 6 views
7

Im Grunde frage ich mich, warum MS beschlossen hat, einen Enumerator zu implementieren, der nur vorwärts geht: MoveNext().Was ist der Grund für IEnumerable/IEnumerable <T> Schnittstellen, nur MoveNext zu haben?

Ist es nicht flexibler, auch MovePrevious für diese weit verbreitete Schnittstelle im gesamten .NET Framework zu erzwingen?

Ich kann mir vorstellen, dass es die Linq.Reverse viel einfacher zu implementieren für MS und effizienter in der Leistung, aber ich bin mir nicht sicher, ob dies andere Dinge langsamer macht oder einen massiven Aufwand für alles andere.

Jeder mit mehr Wissen in diesem Thema kann mehr Informationen zu diesem bitte geben? d.h. die Vor- und Nachteile, MovePrevious in IEnumerable/IEnumerable<T> zu haben oder nicht.

Antwort

6

Es gibt viele Arten von Daten, bei denen es keinen Sinn macht, ein MovePrevious zu haben. Wenn Sie z. B. Daten von einer Netzwerkverbindung empfangen, würde das Bereitstellen eines MovePrevious-Objekts das Zwischenspeichern des gesamten Streams erfordern, für den Fall, dass Sie diese Methode aufgerufen haben. Dies würde große Mengen an Speicher verschwenden.

Nachdem das gesagt wurde, könnte es nützlich sein, einen unterschiedlichen Typ zu haben, der sowohl MoveNext als auch MovePrevious unterstützt (eine doppelt verknüpfte Liste könnte dies zum Beispiel unterstützen).

+5

ITraversable ? :) –

+0

@MarkSimpson: Ich hätte gerne eine 'Skip'-Methode zusammen mit einer Eigenschaft gesehen, die anzeigen würde, ob sie negative Werte akzeptieren könnte. Kein vernünftiger "IEnumerator" sollte Schwierigkeiten bei der Implementierung einer "skip-forward-N-items" -Methode haben, und bei baumbasierten Auflistungen könnte das Abrufen von M-Elementen aus einer Sammlung von N O (M + logN) anstatt O sein (MlogN) oder O (N), die ansonsten die besten sind, die über 'IList ' erreicht werden können. – supercat

1

Die Implementierung eines MovePrevious würde es viel schwerer Schnittstelle machen. Während für einige Quellen (Array, Container-Klassen) ein MovePrevious trivial wäre, würde es für viele andere Quellen eine teure Pufferung erfordern oder diese Quellen ausschließen. Netzwerkströme und Datenbankverbindungen unterstützen keine Suchvorgänge.

21

IEnumerable[<T>] repräsentiert eine Sequenz von Daten, keine zufällige Zugriffsliste. Nicht alle Sequenzen können umgekehrt oder sogar wiederholt werden. Sequenzen basierend auf Netzwerk-Streams, Datenbankzugriff, etc. - oder diese Schönheit:

IEnumerable<int> GetData() { 
    Random rand = new Random(); 
    while(true) { yield return rand.Next(); } 
} 

Das Beste, was Sie tun können, sind wieder von vorne anfangen - nicht von Reset() Aufruf (die veraltet ist), sondern durch einen frischen enumerator stattdessen bekommen .

Auch ohne Random ist es trivial, mit einfachen Sequenzen zu kommen, die kann nicht umgekehrt werden (ohne Pufferung und Umkehrung des Puffers). Für was Sie wollen, betrachten Sie stattdessen IList[<T>] - Sie können Daten über den Indexer in jeder bestellen zugreifen.

+0

Danke Marc, wenn Sie puffern, was auch ein anderes IEnumerable sein könnte, könnte es ein Overhead sein, den nicht jeder möchte, oder? –

+1

In der Tat - Pufferoperationen nicht aufrufen ('Reverse',' OrderBy', 'GroupBy'), wenn Sie eine große (oder unendliche) Sequenz haben ;-p –

+0

Für einige Arten von Sammlungen (zB eine Baum- oder Skip-Liste) wäre eine auffindbare Aufzählung effizienter als eine "IList ". Angesichts der Tatsache, dass jede sinnvolle Implementierung von IEnumerator ein "SkipAhead (int N)" implementieren könnte, da 'N' zu' MoveNext' aufruft, aber viele es schneller implementieren könnten, hätte es als Teil von 'IEnumerator ' erscheinen müssen sinnvoll. – supercat

0

Es ist mit dem Muster in Bezug auf "Ausbeute" zu tun. Effektiv sind Aufzählungen die einfachste mögliche Sammlung, Sie beginnen einfach an der Front und nehmen bis zum Ende. Dies bedeutet, dass sie Code zur Implementierung der Enumeration sehr einfach sind.

Sie können auch Funktionscode Iteratoren haben, die nie als solche „Ende“, zum Beispiel

yield value++; 

Sie einfach erhöhenden Nummern erhalten, bis Sie aufhören.

+1

Tatsächlich braucht es überhaupt kein "Ende" zu geben. Betrachte 'while (true) {yield return Random.Next(); } '. –

+3

Das Design von IEnumerator ist bereits seit mehreren Jahren verfügbar. –

+0

"Ausbeute" macht es einfach, es zu implementieren, aber das Konzept (von "möglichst einfach Sammlung") ist genauso alt. Könnte besser formuliert werden. –

1

Benutzerfreundlichkeit ist nicht der einzige Faktor beim Entwerfen einer Schnittstelle ... Sie müssen berücksichtigen, wie vielseitig es ist und welche Einschränkungen Sie hinzufügen.

Es gibt Sequenzen, die nicht wiedergegeben werden können, und Sie würden viele unnötige Anforderungen hinzufügen, um es zu implementieren.

Es gibt andere Schnittstellen neben ihm, wie IList, IQueryable. Die Verwendung des am besten geeigneten Szenarios teilt auch die Art der Verwendung mit, die es haben sollte.