2008-10-24 8 views

Répondre

67

Fondamentalement, c'était un oubli. En C# 1.0, foreachjamais appelé Dispose . Avec C# 1.2 (introduit dans VS2003 - il n'y a pas de 1.1, bizarrement) foreach a commencé à vérifier dans le bloc finally si l'itérateur a implémenté IDisposable - ils ont dû le faire de cette façon, parce qu'étendre rétrospectivement IEnumerator étendre IDisposable aurait brisé l'implémentation de tous IEnumerator. S'ils avaient conclu qu'il est utile pour foreach de disposer des itérateurs en premier lieu, je suis sûr que IEnumerator aurait prolongé IDisposable. Lorsque C# 2.0 et .NET 2.0 sont sortis, cependant, ils ont eu une nouvelle opportunité - nouvelle interface, nouvel héritage. Il est beaucoup plus logique d'avoir l'interface IDisposable afin que vous n'ayez pas besoin d'une vérification de l'exécution dans le bloc finally, et maintenant le compilateur sait que si l'itérateur est un IEnumerator<T>, il peut émettre un appel inconditionnel vers Dispose.

EDIT: Il est incroyablement utile que Dispose soit appelée à la fin de l'itération (mais elle se termine).Cela signifie que l'itérateur peut conserver les ressources, ce qui lui permet, disons, de lire un fichier ligne par ligne. Iterator bloque les implémentations Dispose du générateur qui s'assurent que tous les blocs finally pertinents au "point d'exécution actuel" de l'itérateur sont exécutés quand ils sont éliminés - afin que vous puissiez écrire du code normal dans l'itérateur et que le nettoyage se fasse correctement.


Rétrospectivement à la spécification 1.0, il a déjà été spécifié. Je n'ai pas encore été en mesure de vérifier cette déclaration antérieure que l'implémentation 1.0 n'a pas appelé Dispose.

+0

dois-je m'attendre à ce que 'IEnumerable.GetEnumerator' (non générique) soit également' IDisposable'? – Shimmy

+1

@Shimmy: Le code qui accepte des implémentations arbitraires de 'IEnumerable 'non générique est obligé de s'assurer que tout objet jetable retourné par' GetEnumerator' sera éliminé. Code qui ne doit pas être considéré comme brisé. – supercat

+0

@supercact: Depuis que j'ai écrit cette réponse, j'ai trouvé que c'était déjà dans la spécification 1.0. Je n'ai pas réussi à obtenir une installation 1.0 pour vérifier si j'avais raison ou non sur le comportement. Éditera. –

-2

IIRC Le fait d'avoir IEnumerable<T> et IEnumerable est le résultat de IEnumerable antérieurs au modèle de .Net. Je soupçonne que votre question est de la même manière.

0

Est-ce que IEnumerable` hérite d'IDisposing? Selon le réflecteur .NET ou MSDN. Êtes-vous sûr de ne pas le confondre avec IEnumerator? Cela utilise IDisposing parce que c'est seulement pour l'énumération d'une collection et non pour la longévité.

+0

IDisposition? Aussi, voulez-vous dire "Non selon ..."? –

+2

Je vous ai donné un +1 pour neutraliser le -1, puisque votre message a été posté entre le moment où l'affiche originale a incorrectement spécifié IEnumerable, et IEnumerator et était probablement ce qui a incité l'OP à résoudre sa question. Néanmoins, puisque la question a été fixée depuis longtemps, vous pouvez aussi supprimer votre "réponse" car elle n'est certainement plus applicable. – supercat

4

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

Je suppose que l'énumérateur générique < T> hérite de IDisposable donc il ne doit pas y avoir un type d'exécution-check-it peut aller de l'avant et appeler Dispose() qui devrait avoir de meilleures performances car il peut être probablement optimisé si l'énumérateur a une méthode Dispose() vide.

+0

Meilleure réponse que la mienne :( –

0

Un peu difficile d'être définitif à ce sujet, à moins que vous n'obteniez une réponse d'AndersH lui-même ou d'un de ses proches. Cependant, je suppose que cela se rapporte au mot-clé "yield" qui a été introduit en C# en même temps. Si vous regardez le code généré par le compilateur lorsque "return return x" est utilisé, vous verrez la méthode enveloppée dans une classe d'aide qui implémente IEnumerator; avoir IEnumerator descendant de IDisposable assure qu'il peut nettoyer lorsque l'énumération est terminée.

+0

rendement rend juste le compilateur de générer du code pour une machine d'état qui n'a pas besoin de disposer au-delà de la GC –

+0

@marxidad: Entièrement incorrect.Pensez à ce qui se passe si une instruction "using" se produit dans un bloc d'itérateur Voir http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx –

+0

@Jon: Pas tout à fait incorrect Bien que IDisposable ne soit pas strictement nécessaire pour les cas où l'utilisation * n'est pas * utilisée, il est plus simple de tout faire. nouveaux énumérateurs de style jetables et appelez Dispose() à chaque fois juste au cas –

3

Je sais que c'est une vieille discussion mais je reasontly écrit une bibliothèque où je IEnumerable de T/IEnumerator de T où les utilisateurs de la bibliothèque pourraient mettre en œuvre itérateurs personnalisés qu'ils devraient juste mettre en œuvre IEnumerator de T.

J'ai trouvé c'est très étrange que IEnumerator de T hérite de IDisposable. Nous implémentons IDisposable si nous voulons libérer des ressources non désaffectées? Cela ne serait donc pertinent que pour les enquêteurs qui détiennent réellement des ressources non gérées - comme un flux d'E/S etc. Pourquoi ne pas laisser les utilisateurs implémenter IEnumerator de T et IDisposable sur leur énumérateur si cela a du sens? Dans mon livre, cela viole le principe de la responsabilité unique - Pourquoi mélanger la logique de l'énumérateur et la disposition des objets.

+0

Si GetEnumerator renvoie un objet qui nécessite un nettoyage (par exemple parce qu'il lit des lignes de données à partir d'un fichier qui doit être fermé), une entité qui sait quand l'énumérateur n'est plus nécessaire doit avoir un moyen de transmettre cette information une entité qui peut effectuer le nettoyage. '' IDisposable'' se comporte en retrait par rapport au principe de substitution de Liskov, car une usine qui renvoie des choses qui pourraient nécessiter un nettoyage n'est pas substituable à celle qui promet de retourner des choses qui ne le font pas, mais la substitution inverse serait sûre et être légitime. – supercat

+0

J'ai également trouvé 'IDisposable' sur' IEnumerator 'pour être un peu déroutant, j'ai trouvé utile de vérifier comment [' Enumerator' de 'List ' implémenté 'IDisposable'] (https://referencesource.microsoft. com/# mscorlib/system/collections/generic/list.cs, 1154) dans la source .NET. Notez comment 'Enumerator'' struct' a une méthode 'Dispose' mais il n'y a rien dedans. ** Notez que ce comportement 'IDisposable' n'implique aucunement quelque chose comme" A 'List ' devrait disposer ses 'Bitmap's dans' foreach'! ** " – jrh

+0

Relatif: [IEnumerator: Est-il normal d'avoir un Dispose vide? méthode?] (http://stackoverflow.com/questions/3061612/ienumerator-is-it-normal-to-have-an-empty-dispose-method) (réponse: oui, c'est) – jrh

Questions connexes