Pourquoi IEnumerator < T > hériter d'IDisposable alors que le non-générique IEnumerator ne le fait pas?

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

  •  04-07-2019
  •  | 
  •  

Question

J'ai remarqué que le IEnumerator < T > générique hérite d'IDisposable, mais pas l'interface IEnumerator non générique. Pourquoi est-il conçu de cette manière?

En général, nous utilisons l'instruction foreach pour parcourir une instance IEnumerator < T > . Le code généré de foreach a en fait un bloc try-finally qui appelle Dispose () dans finally.

Était-ce utile?

La solution

En gros, c’était un oubli. En C # 1.0, foreach jamais appelé Dispose 1 . Avec C # 1.2 (introduit dans VS2003 - il n’ya pas de 1.1, bizarrement), foreach a commencé à vérifier dans le bloc finally si l’itérateur a implémenté IDisposable - ils devaient le faire de cette façon, car créer IEnumerator extend IDisposable de manière rétrospective aurait cassé l'implémentation de IEnumerator par tout le monde. S'ils avaient compris qu'il était utile que foreach supprime les itérateurs en premier lieu, je suis sûr que IEnumerator aurait étendu IDisposable . .

Cependant, lorsque C # 2.0 et .NET 2.0 sont sortis, ils ont eu une nouvelle opportunité: une nouvelle interface, un nouvel héritage. Il est beaucoup plus logique que l'interface étende IDisposable afin que vous n'ayez pas besoin d'une vérification de l'exécution dans le bloc finally, et le compilateur sait maintenant que si l'itérateur est un IEnumerator < ; T > il peut émettre un appel inconditionnel à Dispose .

EDIT: Il est extrêmement utile que Dispose soit appelé à la fin de l'itération (quelle que soit sa fin). Cela signifie que l'itérateur peut conserver des ressources, ce qui lui permet de lire, par exemple, un fichier ligne par ligne. Les blocs Itérateur génèrent des implémentations Dispose qui garantissent que tous les blocs finally pertinents pour le " point d'exécution actuel ". des itérateurs sont exécutés lorsqu'ils sont supprimés - vous pouvez donc écrire du code normal dans l'itérateur et le nettoyage doit s'effectuer de manière appropriée.

1 En regardant en arrière la spécification 1.0, elle était déjà spécifiée. Je n'ai pas encore été en mesure de vérifier cette déclaration antérieure selon laquelle l'implémentation 1.0 n'a pas appelé Dispose .

Autres conseils

IEnumerable < T > n'hérite pas d'IDisposable. IEnumerator < T > n'hérite cependant pas d'IDisposable, contrairement à IEnumerator non générique. Même si vous utilisez foreach pour un IEnumerable non générique (qui renvoie IEnumerator), le compilateur générera toujours une vérification de IDisposable et appellera Dispose () si l'enumérateur implémente l'interface.

Je suppose que l'énumérateur générique < T > hérite de IDisposable, il n’est donc pas nécessaire d’exécuter une vérification de type au moment de l’exécution. Il suffit d’appeler Dispose (), ce qui devrait offrir de meilleures performances car il peut probablement être optimisé si l’énumérateur a un Dispose vide. ) méthode.

Je sais que c’est une vieille discussion, mais j’ai écrit régulièrement une bibliothèque dans laquelle j’utilisais IEnumerable of T / IEnumerator of T, où les utilisateurs de la bibliothèque pouvaient implémenter des itérateurs personnalisés, mais simplement IEnumerator of T.

J'ai trouvé très étrange que IEnumerator of T hérite d'IDisposable. Nous mettons en œuvre IDisposable si nous voulons libérer des ressources non organisées, non? Cela ne concerne donc que les énumérateurs qui possèdent des ressources non gérées - comme un flux d'E / S, etc. Pourquoi ne pas laisser les utilisateurs implémenter à la fois IEnumerator of T et IDisposable sur leur énumérateur si cela a du sens? Dans mon livre, cela viole le principe de responsabilité unique - Pourquoi mélanger la logique de l’enumérateur et la disposition des objets.

IEnumerable` hérite-t-il de IDisposing? Selon le réflecteur .NET ou MSDN . Etes-vous sûr de ne pas le confondre avec IEnumerator ? Cela utilise IDisposing, car il ne sert qu’à énumérer une collection et non à la longévité.

Il est un peu difficile d’être définitif à ce sujet, à moins d’obtenir une réponse d’AndersH lui-même ou de l'un de ses proches.

Cependant, j’imagine qu’il s’agit du "rendement" mot clé introduit en C # au même moment. Si vous examinez le code généré par le compilateur lorsque "& return; return return x". est utilisé, vous verrez la méthode encapsulée dans une classe d'assistance implémentant IEnumerator; le fait de faire descendre IEnumerator d'IDisposable garantit qu'il peut être nettoyé une fois l'énumération terminée.

IIRC Le fait que IEnumerable < T > et IEnumerable soit le résultat de IEnumerable précédant le modèle de modèle de .Net. Je soupçonne que votre question est de la même manière.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top