Como você projeta um enumerador que retorna (teoricamente) uma quantidade infinita de itens?

StackOverflow https://stackoverflow.com/questions/197297

  •  10-07-2019
  •  | 
  •  

Pergunta

Eu estou escrevendo código que é semelhante à seguinte:

public IEnumerable<T> Unfold<T>(this T seed)
{
    while (true)
    {
        yield return [next (T)object in custom sequence];
    }
}

Obviamente, este método nunca vai voltar. (O compilador C # silenciosamente permite isso, enquanto R # me dá a advertência "função nunca retorna" .)

De um modo geral, é mau design para fornecer um enumerador que retorna um número infinito de itens, sem fornecer uma maneira de parar enumerando?

Há considerações especiais para este cenário? Mem? Perf? Outras pegadinhas?

Se nós sempre fornecer uma condição de saída, que são as opções? Por exemplo:

  • um objeto do tipo T que representa o limite inclusiva ou exclusiva
  • a Predicate<T> continue (como TakeWhile faz)
  • a contagem (como Take faz)
  • ...

Devemos contar com os usuários que chamam Take(...) / TakeWhile(...) após Unfold(...)? (Talvez a opção preferida, uma vez que aproveita o conhecimento Linq existente.)

Você responder a esta pergunta de forma diferente se o código estava indo para ser publicado em uma API pública, seja como está (genérico) ou como uma implementação específica desse padrão?

Foi útil?

Solução

Enquanto você documentar de forma muito clara que o método nunca vai terminar a iteração (o próprio método retorna muito rapidamente, é claro), então eu acho que é bom. Na verdade, ele pode fazer alguns algoritmos muito mais puro. Eu não acredito que haja qualquer memória significativa / perf implicações -. Embora se referir a um objeto "caro" dentro do seu iterador, essa referência será capturado

Há sempre maneiras de abusar de APIs: desde que seus documentos são claras, eu acho que é bom

.

Outras dicas

"De modo geral, é ruim desing para proporcionar um enumerador que retorna uma quantidade infinita de itens, sem fornecendo uma maneira de parar enumerando? "

O consumidor do código, pode sempre parada enumerando (usando pausa por exemplo, ou outros meios). Se seus retornos enumerador e sequência infinita, isso não significa que o cliente do recenseador é de alguma forma forçados a nunca quebrar enumeração, na verdade, você não pode fazer um enumerador que é garantido para ser totalmente enumerados por um cliente.

Devemos contar com os usuários de chamadas Tome (...) / TakeWhile (...) depois Desdobrar(...)? (Talvez o preferido opção, uma vez que aproveita existente conhecimento Linq.)

Sim, contanto que você especifique claramente em sua documentação que o enumerador retorna e sequência infinita e quebra de enumeração é responsabilidade do chamador, tudo deve estar bem.

Voltando sequências infinitas não é uma má idéia, linguagens de programação funcionais ter feito isso por um longo tempo agora.

Eu concordo com Jon. Compilador transforma o seu método para classe implementando máquina de estado simples que mantém referência ao valor atual (valor ou seja, que será devolvido via Current propriedade). Eu usei esta abordagem várias vezes para simplificar o código. Se você claramente o comportamento do método documento deve funcionar muito bem.

Eu não usaria um enumerador infinito em uma API pública. Programadores C #, inclusive eu, são muito usados ??para o loop foreach. Isso também seria consistente com o .NET Framework; Observe como os métodos Enumerable.Range e Enumerable.Repeat tomar um argumento para limitar o número de itens na Enumerable. Microsoft escolheu usar Enumerable.Repeat ( " '10) em vez de Enumerable.Repeat ('") .Pegue (10) para evitar a enumeração infinita e gostaria de aderir a suas escolhas de design.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top