Penso que uma vantagem do modelo C # / .NET sobre o MoveNext()
modelo Java hasNext()
é que o primeiro implica que algum trabalho possa ser feito. O hasNext()
método implica uma simples verificação de estado. Mas e se você estiver iterando um fluxo lento e precisar acessar um banco de dados para determinar se há resultados? Nesse caso, a primeira chamada para hasNext()
pode ser uma operação longa de bloqueio. A nomeação do hasNext()
método pode ser muito enganadora quanto ao que realmente está acontecendo.
Para um exemplo do mundo real, esse problema de design me incomodou ao implementar as APIs de Extensões Reativas (Rx) da Microsoft em Java. Especificamente, para que o iterador criado por IObservable.asIterable()
funcione corretamente, o hasNext()
método precisará bloquear e aguardar a chegada da próxima notificação de item / erro / conclusão. Isso não é intuitivo: o nome implica uma simples verificação de estado. Compare isso ao design do C #, onde MoveNext()
seria necessário bloquear, o que não é um resultado totalmente inesperado quando você está lidando com um fluxo avaliado preguiçosamente.
O que quero dizer é o seguinte: o modelo "next-iterator" é preferível ao modelo "this-iterator" porque o modelo "this-iterator" é frequentemente uma mentira: você pode ser solicitado a buscar previamente o próximo elemento para verificar o estado do iterador. Um desenho que comunique claramente essa possibilidade é preferível, na minha opinião. Sim, as convenções de nomenclatura de Java seguem o modelo "próximo", mas as implicações comportamentais são semelhantes às do modelo "this-iterator".
Esclarecimento: Talvez contrastar Java e C # fosse uma péssima escolha, porque o modelo de Java é enganoso. Considero que a distinção mais importante nos modelos apresentados pelo OP é que o modelo "this-iterator" desacopla a lógica "é válida" da lógica "recuperar atual / próximo elemento", enquanto uma implementação ideal de "próximo iterador" combina essas operações. Eu acho que é apropriado combiná-los porque, em alguns casos, determinar se o estado do iterador é válido requer a pré-busca do próximo elemento , para que a possibilidade seja tornada o mais explícita possível.
Não vejo uma diferença significativa de design entre:
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!)
}
Mas vejo uma diferença significativa aqui:
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)
}
Nos exemplos isValid()
e hasNext()
, a operação implícita como uma verificação de estado rápida e sem bloqueio pode ser de fato uma operação lenta e de bloqueio . No moveNext()
exemplo, a maior parte do trabalho está sendo realizada no método que você esperaria, independentemente de estar lidando com um fluxo avaliado avidamente ou preguiçosamente.