Would C # beneficiar de distinções entre tipos de recenseadores, como iteradores C ++?

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

  •  06-07-2019
  •  | 
  •  

Pergunta

Eu estive pensando sobre o método IEnumerator.Reset(). Eu li na documentação do MSDN que só lá para interoperabilidade COM. Como um programador C ++ parece-me como um IEnumerator que suporta Reset é o que eu chamaria de um encaminhar iterador , enquanto um IEnumerator que não faz Reset apoio é realmente um entrada iterador .

Assim, a primeira parte da minha pergunta é, esse entendimento correto?

A segunda parte da minha pergunta é, seria de qualquer benefício em C # se havia uma distinção entre iteradores de entrada e iteradores para a frente (ou "recenseadores" se você preferir)? Será que não ajudar a eliminar alguma confusão entre os programadores, como o encontrado neste questão SO sobre clonagem iterators ?

EDIT: Esclarecimento sobre iteradores para a frente e de entrada. Um iterador de entrada só garante que você pode enumerar os membros de uma coleção (ou a partir de uma função de gerador ou um fluxo de entrada) apenas uma vez. Isto é exatamente como IEnumerator funciona em C #. Querendo ou não você pode enumerar uma segunda vez, é determinada por se ou não Reset é suportado. Um iterador para a frente, não tem essa restrição. Você pode enumerar sobre os membros quantas vezes você quiser.

Alguns programadores C # não underestand por um IEnumerator não pode ser usado de forma confiável em um algoritmo de multipass. Considere o seguinte caso:

void PrintContents(IEnumerator<int> xs)
{
  while (iter.MoveNext())
    Console.WriteLine(iter.Current); 
  iter.Reset();
  while (iter.MoveNext())
    Console.WriteLine(iter.Current); 
}

Se chamarmos PrintContents neste contexto, não há problema:

List<int> ys = new List<int>() { 1, 2, 3 }
PrintContents(ys.GetEnumerator()); 

No entanto olhar para o seguinte:

IEnumerable<int> GenerateInts() {   
  System.Random rnd = new System.Random();
  for (int i=0; i < 10; ++i)
    yield return Rnd.Next();
}

PrintContents(GenerateInts());

Se o IEnumerator suportado Reset, ou seja suportado algoritmos multi-passagem, em seguida, cada vez que você iterado sobre a coleta seria diferente. Este seria indesejável, porque seria um comportamento surpreendente. Este exemplo é um pouco fingido, mas ocorre no mundo real (por exemplo, a leitura de fluxos de arquivo).

Foi útil?

Solução

É uma pergunta interessante. Minha opinião é que, naturalmente, C # seria benefício . No entanto, não seria fácil adicionar.

A distinção existe em C ++, devido à sua muito sistema tipo mais flexível. Em C #, você não tem uma maneira genérica robusta para objetos de clone, o que é necessário para representar iteradores para a frente (para suportar multi-pass iteração). E, claro, para que isso seja realmente útil, você também precisa apoiar bidirecionais e de acesso aleatório iteradores / recenseadores. E para obtê-los todos a trabalhar bem, você realmente precisa de alguma forma de pato-digitando, como modelos C ++ ter.

Em última análise, os escopos dos dois conceitos são diferentes.

Em C ++, iterators supostamente representam tudo o que você precisa saber sobre uma faixa de valores. não dado um par de iteradores, eu faço necessidade o recipiente original. Eu posso tipo, eu posso procurar, eu posso manipular e copiar elementos tanto quanto eu gostaria. O recipiente original está fora da imagem.

Em C #, os entrevistadores não são destinadas a fazer tanto. Em última análise, eles estão apenas projetado para permitir que você executar através da seqüência de uma forma linear.

Quanto Reset(), é amplamente aceito que foi um erro para adicioná-lo em primeiro lugar. Se ele tinha trabalhado, e foi implementado corretamente, então sim, você poderia dizer que o seu entrevistador era análoga de transmitir iteradores, mas, em geral, é melhor ignorá-la como um erro. E então todos os recenseadores são semelhantes apenas para iteradores de entrada.

Infelizmente.

Outras dicas

Reset foi um grande erro. Tretas em Reset. Na minha opinião, a maneira correta para refletir a distinção que você está fazendo entre "iteradores para a frente" e "iteradores de entrada" no sistema tipo .NET é com a distinção entre IEnumerable<T> e IEnumerator<T>.

Veja também este resposta , onde Eric Lippert da Microsoft (em uma capactiy não oficial, sem dúvida, o meu ponto é apenas que ele de alguém com mais credenciais do que eu tenho que fazer a alegação de que isso foi um erro design) faz uma observação semelhante nos comentários. Além disso, também consulte sua incrível blogue .

Vindo da perspectiva C #:

Você quase nunca uso IEnumerator diretamente. Normalmente, você faz uma declaração foreach, que espera um IEnumerable.

IEnumerable _myCollection;
...
foreach (var item in _myCollection) { /* Do something */ }

Você não passar em torno IEnumerator quer. Se você quer passar uma coleção que precisa de iteração, você passa IEnumerable. Desde IEnumerable tem uma única função, que retorna um IEnumerator, ele pode ser usado para iterar os tempos coleção múltiplas (várias passagens).

Não há nenhuma necessidade para uma função Reset() em IEnumerator porque se você quer começar de novo, você simplesmente jogar fora o antigo (lixo coletado) e obter um novo.

O framework .NET beneficiaria imensamente se houvesse um meio de pedir a um IEnumerator<T> sobre o que habilidades que poderia apoiar e que promete ele poderia fazer. Tais características também seria útil para IEnumerable<T>, mas ser capaz de fazer as perguntas de um entrevistador permitiria código que pode receber um recenseador do wrappers como ReadOnlyCollection de usar a coleção subjacente em melhorar as formas sem ter de envolver o invólucro.

Dado qualquer enumerador para um conjunto que é capaz de ser enumerados na sua totalidade e não é muito grande, pode-se produzir a partir dele um IEnumerable<T> que sempre se obter a mesma sequência de itens (especificamente o conjunto de itens restantes na recenseador) lendo seu conteúdo inteiro para um array, eliminação e descartando o recenseador, e obter uma recenseadores do array (usando que no lugar do recenseador abandonado original), envolvendo a matriz em um ReadOnlyCollection<T>, e retornando isso. Embora tal abordagem iria trabalhar com qualquer tipo de coleção enumerable cumprimento dos critérios acima, seria terrivelmente ineficiente com a maioria deles. Ter um meio de pedir um enumerador para produzir seu conteúdo restantes em uma IEnumerable<T> imutável permitiria que muitos tipos de recenseadores para executar a ação indicada muito mais eficiente.

Eu não penso assim. Eu chamaria IEnumerable um iterador para a frente, e um iterador de entrada. Ele não permite que você vá para trás, ou modificar a coleção subjacente. Com a adição da palavra-chave foreach, iteradores são quase uma não-pensamento na maioria das vezes.

Opinião: A diferença entre iteradores de entrada ( obter cada um ) iteradores de saída vs. ( fazer algo para cada um ) é trivial demais para justificar uma adição ao quadro. Além disso, a fim de fazer um iterador de saída, você precisa passar um delegado para o iterador. A entrada iterador parece mais natural para programadores C #.

Há também IList<T> se o programador quer acesso aleatório.

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