Por que IEnumerator herdam IDisposable enquanto o IEnumerator não-genérico não?
-
04-07-2019 - |
Pergunta
Eu observei que as herda IEnumerator<T>
genéricos de IDisposable, mas a interface não genérica IEnumerator não. Por que é projetado desta forma?
Normalmente, usamos instrução foreach para percorrer uma instância IEnumerator<T>
. O código gerado de foreach, na verdade, tem-tentar finalmente bloquear o que invoca Descarte () em finalmente.
Solução
Basicamente, foi um descuido. No C # 1.0, foreach
não chamado Dispose
1 . Com C # 1.2 (introduzido no VS2003 - não há 1.1, bizarramente) foreach
começou a verificar no bloco finally
ou não o iterador implementado IDisposable
- eles tinham que fazer isso dessa forma, porque retrospectivamente fazendo IEnumerator
estender IDisposable
teria quebrado a implementação de todos de IEnumerator
. Se tivessem trabalhado para fora que é útil para foreach
de dispor de iteradores, em primeiro lugar, tenho certeza IEnumerator
teria IDisposable
estendida.
Quando C # 2.0 e .NET 2.0 saiu, no entanto, tiveram uma nova oportunidade - nova interface, novos herança. Faz muito mais sentido ter a interface estender IDisposable
de modo que você não precisa de um check-tempo de execução no bloco finally, e agora o compilador sabe que se o iterador é um IEnumerator<T>
pode emitir uma chamada incondicional de Dispose
.
EDIT: É incrivelmente útil para Dispose
a ser chamado no final da iteração (no entanto, termina). Isso significa que o iterador pode segurar recursos - o que o torna viável para que, por exemplo, ler um arquivo linha por linha. Iterator blocos gerar implementações Dispose
que se certificar de que todos os blocos finally
relevantes para o "ponto atual de execução" do iterador são executados quando é descartado -. Assim você pode escrever código normal dentro do iterador e clean-up deve acontecer de forma adequada
1 para trás Olhando para a especificação 1.0, que já foi especificado. Eu ainda não ter sido capaz de verificar esta afirmação anterior de que a implementação 1.0 não chamar Dispose
.
Outras dicas
IEnumerable
Eu acho que o genérico Enumerator
Eu sei que esta é uma discussão antiga, mas eu reasontly escreveu uma biblioteca onde eu costumava IEnumerable de T / IEnumerator de T onde os usuários da biblioteca poderia implementar iteradores personalizados devem apenas implementar IEnumerator de t.
Eu achei muito estranho que IEnumerator da T iria herdar de IDisposable. Nós implementamos IDisposable se queremos liberar recursos unmanged certo? Por isso, só seria relevante para recenseadores que realmente detêm recursos não gerenciados - como um fluxo de IO etc. Porque não basta permitir que os usuários implementar tanto IEnumerator de T e IDisposable em seu entrevistador se faz sentido? Em meu livro isso viola o princípio da responsabilidade individual - Por que misturar lógica recenseador e objetos eliminação.
O IEnumerable` herdar IDisposing? De acordo com o refletor .NET ou MSDN . Tem certeza de que não está confundindo-a com IEnumerator ? Que usa IDisposing porque só para enumerar uma coleção e não significava para a longevidade.
Um pouco difícil de ser definitivo sobre isso, a não ser que você consiga obter uma resposta do próprio AndersH, ou alguém próximo a ele.
No entanto, o meu palpite é que ele se relaciona com a palavra-chave "rendimento" que foi introduzido em C #, ao mesmo tempo. Se você olhar para o código gerado pelo compilador quando "rendimento retorno x" é usado, você verá o método embrulhado em uma classe auxiliar que implementa IEnumerator; tendo IEnumerator descerá do garante IDisposable que ele pode limpar quando enumeração está completa.
IIRC A coisa toda sobre ter IEnumerable<T>
e IEnumerable
é resultado de IEnumerable
predating coisas modelo de .Net. Eu suspeito que sua pergunta é da mesma forma.