Por que Ienumerable herda de Ienumerable?
-
03-07-2019 - |
Pergunta
Esta pode ser uma pergunta antiga: por que IEnumerable<T>
herdar de IEnumerable
?
É assim que o .NET faz, mas traz um pouco de problema. Toda vez que escrevo implementos de classe IEumerable<T>
, Eu tenho que escrever dois GetEnumerator()
funções, uma para IEnumerable<T>
e o outro para IEnumerable
.
E, IList<T>
não herde do ilista.
Eu não sei por que IEnumerable<T>
foi projetado de outra maneira.
Solução
Direto da boca do cavalo (Hejlsberg):
Idealmente, todas as interfaces genéricas de coleção (por exemplo
ICollection<T>
,IList<T>
) herdaria de suas contrapartes não genéricas, de modo que instâncias genéricas da interface pudessem ser usadas com código genérico e não genérico. Por exemplo, seria conveniente se umIList<T>
poderia ser passado para o código que espera umIList
.Como se vê, a única interface genérica para a qual isso é possível é
IEnumerable<T>
, porque apenasIEnumerable<T>
é contra-variante: emIEnumerable<T>
, o parâmetro de tipo t é usado apenas nas posições "Saída" (valores de retorno) e não nas posições "entrada" (parâmetros).ICollection<T>
eIList<T>
Use t nas posições de entrada e saída, e essas interfaces são, portanto, invariantes. (Como um aparte, eles teriam sido contra-variantes se T fosse usado apenas em posições de entrada, mas isso realmente não importa aqui.)<... Snip ...>
Então, para responder sua pergunta, IEnumerable<T>
herda de IEnumerable
Porque pode! :-)
Outras dicas
A resposta para IEnumerable
é: "Porque isso pode sem afetar a segurança do tipo".
IEnumerable
é uma interface "readonly" - então não importa que a forma genérica seja mais específica que a forma não -ngonérica. Você não quebra nada implementando os dois. IEnumerator.Current
retorna object
, enquanto IEnumerator<T>.Current
retorna T
- Tudo bem, como você sempre pode legitimamente se converter para object
, embora possa significar boxe.
Compare isso com IList<T>
e IList
- você pode ligar Add(object)
em um IList
, enquanto isso pode muito bem ser inválido para qualquer particular IList<T>
(qualquer outra coisa senão IList<object>
na verdade).
Brad Abram's blogou com a resposta de Anders sobre esta mesma pergunta.
É para compatibilidade com versões anteriores. Se você ligar para uma função .NET 1.1 que espera uma baunilha que você possa passar no seu genérico Ienumerable.
Luckilly, o ienumerador genérico herda do iEenumerator de estilo antigo
Normalmente, implemento um método privado que retorna um enumerador e depois o passo para o método Getenumerator antigo e novo.
private IEnumerator<string> Enumerator() {
// ...
}
public IEnumerator<string> GetEnumerator() {
return Enumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return Enumerator();
}
Isso é para que funcione com classes que não suportam genéricas. Além disso, os genéricos do .NET não permitem que você faça coisas como elenco ilistu003Clong> como ilistau003Cint> , portanto, versões não genéricas de interfaces podem ser bastante úteis quando você precisa de uma classe ou interface base fixa.