Ich denke, ein Vorteil des C # /. NET- MoveNext()
Modells gegenüber dem Java- hasNext()
Modell besteht darin, dass das erstere impliziert, dass einige Arbeiten durchgeführt werden können. Die hasNext()
Methode impliziert eine einfache Zustandsprüfung. Aber was ist, wenn Sie einen faulen Stream iterieren und in eine Datenbank gehen müssen, um festzustellen, ob Ergebnisse vorliegen? In diesem Fall kann der erste Aufruf hasNext()
eine lange Blockierungsoperation sein. Die Benennung der hasNext()
Methode kann sehr irreführend sein, was tatsächlich vor sich geht.
Für ein Beispiel aus der Praxis hat mich dieses Designproblem bei der Implementierung der Rx-APIs (Reactive Extensions) von Microsoft in Java gebissen. Insbesondere für den Iterator, der von erstellt wurde, IObservable.asIterable()
um ordnungsgemäß zu funktionieren, hasNext()
müsste die Methode blockieren und auf das Eintreffen der nächsten Element- / Fehler- / Abschlussbenachrichtigung warten. Das ist kaum intuitiv: Der Name impliziert eine einfache Zustandsprüfung. Vergleichen Sie dies mit dem C # -Design, bei MoveNext()
dem blockiert werden müsste, was kein völlig unerwartetes Ergebnis ist, wenn Sie mit einem träge ausgewerteten Stream arbeiten.
Mein Punkt ist folgender: Das Modell "nächster Iterator" ist dem Modell "dieser Iterator" vorzuziehen, da das Modell "dieser Iterator" häufig eine Lüge ist: Möglicherweise müssen Sie das nächste Element vorab abrufen, um es zu überprüfen der Zustand des Iterators. Ein Design, das diese Möglichkeit klar kommuniziert, ist meiner Meinung nach vorzuziehen. Ja, die Namenskonventionen von Java folgen zwar dem "nächsten" Modell, aber die Auswirkungen auf das Verhalten ähneln denen des "this-iterator" -Modells.
Klarstellung: Vielleicht war es eine schlechte Wahl, Java und C # gegenüberzustellen, weil Javas Modell täuscht. Ich betrachte die wichtigste Unterscheidung in den vom OP vorgestellten Modellen darin, dass das "this-iterator" -Modell die "is valid" -Logik von der Logik "current / next element abrufen" entkoppelt, während es sich um eine ideale "next-iterator" -Implementierung handelt kombiniert diese Operationen. Ich halte es für angebracht, sie zu kombinieren, da in einigen Fällen das Vorabrufen des nächsten Elements erforderlich ist, um festzustellen, ob der Status des Iterators gültig ist , sodass die Möglichkeit so explizit wie möglich gemacht werden sollte.
Ich sehe keinen signifikanten Designunterschied zwischen:
while (i.isValid()) { // do we have an element? (implied as fast, non-blocking)
doSomething(i.current()); // retrieve the element (may be slow!)
i.next();
}
// ...and:
while (i.hasNext()) { // do we have an element? (implied as fast, non-blocking)
doSomething(i.next()); // retrieve the element (may be slow!)
}
Aber ich sehe hier einen bedeutenden Unterschied:
while (i.hasNext()) { // do we have an element? (implied as fast, non-blocking)
doSomething(i.next()); // retrieve the element (may be slow!)
}
// ...and:
while (i.moveNext()) { // fetch the next element if it exists (may be slow!)
doSomething(i.current()); // get the element (implied as fast, non-blocking)
}
In den beiden isValid()
und hasNext()
Beispielen wird die Operation , die eine schnelle und nicht-blockierenden Zustand Prüfung impliziert werden , kann in der Tat eine langsame, Sperrbetrieb . In diesem moveNext()
Beispiel wird der größte Teil der Arbeit in der erwarteten Methode ausgeführt, unabhängig davon, ob es sich um einen eifrig oder träge bewerteten Stream handelt.