Pourquoi IEnumerable hérite-t-il de IEnumerable?

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 un IList au code qui attend un IList .

Il s’avère que la seule interface générique pour laquelle cela est possible est IEnumerable , car seul IEnumerable est une variante: Dans IEnumerable , 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 et IList 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.Current 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 en tant que IList . Par conséquent, des versions non génériques d’interfaces peuvent s’avérer très utiles lorsque vous avez besoin d’une classe ou d’une interface de base fixe.