C’est peut-être une vieille question: pourquoi IEnumerable
hérite-t-il de IEnumerable
?
C’est le cas de .NET, mais cela crée un petit problème. Chaque fois que j’écris une classe implémentant IEumerable
, je dois écrire deux fonctions GetEnumerator()
, une pour IEnumerable
et l’autre pour IEnumerable
.
Et IList
n’hérite pas de IList.
Je ne sais pas pourquoi IEnumerable
est conçu d’une autre manière.
Tout droit de la bouche du cheval (Hejlsberg):
Idéalement, toutes les interfaces de collection génériques (par exemple,
ICollection
,IList
) hériteraient de leurs équivalents non génériques, de sorte que les instances d’interface génériques pourraient être utilisées avec du code générique et non générique. Par exemple, il serait pratique de pouvoir passer unIList
au code qui attend unIList
.Il s’avère que la seule interface générique pour laquelle cela est possible est
IEnumerable
, car seulIEnumerable
est une variante: DansIEnumerable
, le paramètre de type T n’est utilisé que dans les positions “output” ( valeurs de retour) et non dans les positions “entrée” (parameters).ICollection
etIList
utilisent T dans les positions d’entrée et de sortie. Ces interfaces sont donc invariantes. (En passant, ils auraient été contra-variables si T était utilisé uniquement dans les positions d’entrée, mais cela n’a pas vraiment d’importance ici.)<... snip ...>
Donc, pour répondre à votre question, IEnumerable
hérite de IEnumerable
car il le peut! 🙂
La réponse pour IEnumerable
est: “car cela peut sans affecter la sécurité du type”.
IEnumerable
est une interface “en lecture seule”. Le formulaire générique est donc plus spécifique que le formulaire non générique. Vous ne cassez rien en implémentant les deux. IEnumerator.Current
renvoie un object
, alors que IEnumerator
renvoie un T
– ce n’est pas grave, car vous pouvez toujours légitimement convertir en object
, bien que cela puisse vouloir dire boxing.
Comparez cela avec IList
et IList
– vous pouvez appeler Add(object)
sur un IList
, alors que cela peut être invalide pour un IList
(tout ce qui est autre que IList
).
Brad Abram a publié sur son blog la réponse d’Anders à cette question.
C’est pour la compatibilité ascendante. Si vous appelez une fonction .Net 1.1 qui attend un IEnumerable vanille, vous pouvez transmettre votre IEnumerable générique.
Heureusement, le générique IEnumerator hérite de l’ancien IEnumerator
J’implémente généralement une méthode privée qui renvoie un énumérateur, puis le passe à la fois pour la méthode GetEnumerator de style ancien et nouveau.
private IEnumerator Enumerator() { // ... } public IEnumerator GetEnumerator() { return Enumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return Enumerator(); }
C’est pour que cela fonctionne avec des classes qui ne supportent pas les génériques. De plus, les génériques .NET ne vous permettent pas d’effectuer des opérations telles que la conversion de IList