65 votes

La fonction "Dispose" doit-elle être utilisée uniquement pour les types contenant des ressources non gérées ?

J'ai récemment eu une discussion avec un collègue sur la valeur de l'information. Dispose et les types qui mettent en œuvre IDisposable .

Je pense qu'il y a de la valeur à mettre en œuvre IDisposable pour les types qui doivent se nettoyer le plus rapidement possible, même s'il n'y a pas de ressources non gérées à nettoyer. .

Mon collègue pense différemment ; mise en œuvre IDisposable si vous n'avez pas de ressources non gérées, ce n'est pas nécessaire car votre type sera éventuellement collecté par les ordures.

Mon argument était que si vous aviez une connexion ADO.NET que vous vouliez fermer dès que possible, alors la mise en œuvre de l'option IDisposable y using new MyThingWithAConnection() aurait un sens. Mon collègue m'a répondu que, sous couvert, une connexion ADO.NET est une ressource non gérée . Ma réponse à sa réponse était la suivante tout est finalement une ressource non gérée .

Je suis conscient de la modèle jetable recommandé où vous des ressources libres gérées et non gérées si Dispose s'appelle mais libérer les ressources non gérées uniquement si elles sont appelées via le finalisateur/destructeur. (et j'ai écrit un blog il y a quelque temps sur comment alerter les consommateurs en cas d'utilisation inappropriée de vos types d'IDisposable )

Donc, ma question est la suivante : si vous avez un type qui ne contient pas de ressources non gérées, cela vaut-il la peine d'implémenter l'option IDisposable ?

35voto

Vlad Points 23480

Il existe différentes utilisations valables de IDisposable . Un exemple simple est le maintien d'un fichier ouvert, qui doit être fermé à un moment donné, dès que vous n'en avez plus besoin. Bien sûr, vous pouvez fournir une méthode Close mais l'avoir dans Dispose et en utilisant un modèle comme using (var f = new MyFile(path)) { /*process it*/ } serait plus sûr pour les exceptions.

Un exemple plus populaire serait de tenir un autre IDisposable ressources, ce qui signifie généralement que vous devez fournir votre propre Dispose afin de les éliminer également.

En général, dès que vous voulez avoir une destruction déterministe de quelque chose, vous devez implémenter IDisposable .

La différence entre mon opinion et la vôtre est que je mets en oeuvre IDisposable dès qu'une ressource a besoin déterministe destruction/libération, non nécessaire dans les plus brefs délais . S'en remettre au ramassage des déchets n'est pas une option dans ce cas (contrairement à ce que prétend votre collègue), car il se produit à un moment imprévisible, et peut même ne pas se produire du tout !

Le fait qu'une ressource ne soit pas gérée sous la couverture ne signifie rien : le développeur doit penser en termes de " quand et comment il est bon de disposer de cet objet " plutôt que de " comment cela fonctionne sous la couverture ". L'implémentation sous-jacente peut changer avec le temps de toute façon.

En fait, l'une des principales différences entre C# et C++ est l'absence de destruction déterministe par défaut. Le site IDisposable vient combler le fossé : vous pouvez ordonner la destruction déterministe (bien que vous ne puissiez pas vous assurer que les clients l'appellent ; de la même manière qu'en C++ vous ne pouvez pas être sûr que les clients appellent delete sur l'objet).


Petit complément : quelle est en réalité la différence entre le déterministe libérer les ressources et les libérer dans les plus brefs délais ? En fait, ce sont des notions différentes (mais pas complètement orthogonales).

Si les ressources doivent être libérées de manière déterministe Cela signifie que le code client devrait avoir la possibilité de dire "Maintenant, je veux que cette ressource soit libérée". En fait, ce n'est peut-être pas le le plus tôt possible le moment où la ressource peut être libérée : l'objet qui détient la ressource peut avoir obtenu tout ce dont il a besoin de la ressource, donc potentiellement il peut déjà libérer la ressource. D'un autre côté, l'objet peut choisir de conserver la ressource (généralement non gérée) même après la libération de l'objet. Dispose s'est déroulée, en ne la nettoyant que dans le finaliseur (si le fait de conserver la ressource pendant trop longtemps ne pose pas de problème).

Donc, pour libérer la ressource dans les plus brefs délais à proprement parler, Dispose n'est pas nécessaire : l'objet peut libérer la ressource dès qu'il se rend compte lui-même que la ressource n'est plus nécessaire. Dispose sert cependant d'indice utile pour indiquer que l'objet lui-même n'est plus nécessaire, alors peut-être que les ressources peut être libéré à ce moment-là, le cas échéant.


Un autre ajout nécessaire : les ressources non gérées ne sont pas les seules à nécessiter une désallocation déterministe ! Cela semble être l'un des points clés de la différence d'opinions entre les réponses à cette question. On peut avoir une construction purement imaginative, qui peut avoir besoin d'être libérée de manière déterministe.

Exemples : un droit d'accès à une structure partagée (pensez à Serrure RW ), un gros morceau de mémoire (imaginez que vous gérez manuellement une partie de la mémoire du programme), une licence pour l'utilisation d'un autre programme (imaginez que vous n'êtes pas autorisé à exécuter plus de X copies d'un programme simultanément), etc. Ici, l'objet à libérer n'est pas une ressource non gérée, mais une droite pour faire/utiliser quelque chose, qui est une construction purement interne à la logique de votre programme.


Petit ajout : voici une petite liste d'exemples soignés d'utilisation de l'[ab]utilisation. IDisposable : http://www.introtorx.com/Content/v1.0.10621.0/03_LifetimeManagement.html#IDisposable .

17voto

supercat Points 25534

Je pense que c'est plus utile de penser à IDisposable en termes de responsabilités . Un objet doit implémenter IDisposable s'il a connaissance de quelque chose qui devra être fait entre le moment où il ne sera plus nécessaire et la fin de l'univers (et de préférence le plus tôt possible), et s'il est le seul objet ayant à la fois l'information et l'impulsion pour le faire. Un objet qui ouvre un fichier, par exemple, a la responsabilité de veiller à ce que le fichier soit fermé. Si l'objet disparaissait simplement sans fermer le dossier, ce dernier pourrait ne pas être fermé dans un délai raisonnable.

Il est important de noter que même les objets qui n'interagissent qu'avec des objets gérés à 100 % peuvent faire des choses qui doivent être nettoyées (et devraient utiliser la fonction IDisposable ). Par exemple, un IEnumerator qui s'attache à l'événement "modified" d'une collection devra se détacher quand il ne sera plus nécessaire. Sinon, à moins que l'énumérateur n'utilise une astuce complexe, il ne sera jamais mis à la poubelle tant que la collection est dans le champ d'application. Si la collection est énumérée un million de fois, un million d'énumérateurs seront attachés à son gestionnaire d'événement.

Notez qu'il est parfois possible d'utiliser les finaliseurs pour le nettoyage dans les cas où, pour une raison ou une autre, un objet est abandonné sans que l'on puisse le faire. Dispose ayant été appelé en premier. Parfois, cela fonctionne bien ; parfois, cela fonctionne très mal. Par exemple, même si Microsoft.VisualBasic.Collection utilise un finisseur pour détacher les énumérateurs des événements "modifiés", en essayant d'énumérer un tel objet des milliers de fois sans qu'il n'y ait d'intervention. Dispose ou de garbage-collection le rendra très lent - plusieurs ordres de grandeur plus lent que les performances qui résulteraient de l'utilisation de l'option Dispose correctement.

9voto

Kevin Points 57797

Donc, ma question est, si vous avez un type qui ne contient pas de ressources non gérées, cela vaut-il la peine d'implémenter IDisposable ?

Lorsque quelqu'un place une interface IDisposable sur un objet, cela m'indique que le créateur a l'intention de faire quelque chose avec cette méthode ou qu'il pourrait le faire dans le futur. J'appelle toujours dispose dans ce cas, juste pour être sûr. Même si cela ne fait rien pour le moment, cela pourrait le faire dans le futur, et cela craint d'avoir une fuite de mémoire parce qu'ils ont mis à jour un objet, et que vous n'avez pas appelé Dispose quand vous avez écrit le code la première fois.

En vérité, c'est une question de jugement. Vous ne voulez pas trop l'implémenter, parce qu'à ce moment-là, pourquoi s'embêter à avoir un ramasse-miettes ? Pourquoi ne pas simplement disposer manuellement de chaque objet. S'il y a une possibilité que vous ayez besoin de disposer de ressources non gérées, alors ce n'est peut-être pas une mauvaise idée. Tout dépend, si les seules personnes qui utilisent votre objet sont les personnes de votre équipe, vous pouvez toujours les suivre plus tard et leur dire, "Hey ceci doit utiliser une ressource non gérée maintenant. Nous devons parcourir le code et nous assurer que nous avons fait le ménage." Si vous publiez ces données pour que d'autres organisations les utilisent, c'est différent. Il n'y a pas de moyen facile de dire à tous ceux qui ont implémenté cet objet, "Hey, vous devez être sûr que cet objet est maintenant disposé". Laissez-moi vous dire qu'il y a peu de choses qui rendent les gens plus furieux que de mettre à jour un assemblage tiers pour découvrir que ce sont eux qui ont changé leur code et qui ont fait que votre application a des problèmes de mémoire.

Mon collègue m'a répondu qu'en réalité, une connexion ADO.NET est une ressource gérée. ressource gérée. Ma réponse à sa réponse était que tout, en fin de compte est une ressource non gérée.

Il a raison, c'est une ressource gérée en ce moment. Est-ce qu'ils changeront un jour ? Qui sait, mais cela ne fait pas de mal de l'appeler. Je n'essaie pas de deviner ce que fait l'équipe ADO.NET, donc s'ils l'intègrent et qu'il ne fait rien, c'est très bien. Je l'appellerai quand même, car une ligne de code ne va pas affecter ma productivité.

Un autre scénario peut également se produire. Disons que vous renvoyez une connexion ADO.NET à partir d'une méthode. Vous ne savez pas d'emblée si cette connexion ADO est l'objet de base ou un type dérivé. Vous ne savez pas si l'implémentation de IDisposable est soudainement devenue nécessaire. Je l'appelle toujours, quoi qu'il arrive, parce que traquer les fuites de mémoire sur un serveur de production, ça craint quand il plante toutes les 4 heures.

6voto

Adam Robinson Points 88472

Bien qu'il y ait déjà de bonnes réponses à cette question, je voulais juste expliciter quelque chose.

Il existe trois cas de figure pour la mise en œuvre IDisposable :

  1. Vous utilisez directement des ressources non gérées. Cela implique généralement de récupérer une IntPrt ou toute autre forme de gestion d'un appel P/Invoke qui doit être libéré par un autre appel P/Invoke.
  2. Vous utilisez d'autres IDisposable les objets et doivent être responsables de leur disposition
  3. Vous en avez un autre besoin ou une autre utilité, y compris la commodité de l'utilisation de l'appareil. using bloc.

Bien que je sois peut-être un peu partial, vous devriez vraiment lire (et montrer à votre collègue) le Wiki StackOverflow sur IDisposable .

5voto

Tigran Points 41381

Non, c'est no uniquement pour les ressources non gérées.

Il est suggéré comme un mécanisme intégré de nettoyage de base appelé par le cadre, qui vous donne la possibilité de nettoyer n'importe quelle ressource que vous voulez, mais il est le mieux adapté est naturellement la gestion des ressources non gérées.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X