113 votes

Quelle est la différence entre l'utilisation d'IDisposable et d'un destructeur en C# ?

Quand dois-je implémenter IDispose sur une classe plutôt qu'un destructeur ? Je lis cet article mais je ne comprends toujours pas le problème.

Je suppose que si j'implémente IDispose sur un objet, je peux explicitement le "détruire" au lieu d'attendre que le ramasseur d'ordures le fasse. Est-ce exact ?

Cela signifie-t-il que je dois toujours appeler explicitement Dispose sur un objet ? Quels sont les exemples les plus courants ?

6 votes

En effet, vous devez appeler Dispose sur chaque objet Disposable. Vous pouvez le faire facilement en utilisant la fonction using construire.

0 votes

Ah, ça a du sens. Je me suis toujours demandé pourquoi l'instruction "using" était utilisée pour les flux de fichiers. Je sais que cela avait quelque chose à voir avec la portée de l'objet, mais je ne l'avais pas mis en relation avec l'interface IDisposable.

6 votes

Un point important à retenir est qu'un finisseur doit jamais d'accéder à tout membre géré d'une classe, car ces membres peuvent ne plus être des références valides.

143voto

Marc Gravell Points 482669

Un finaliseur (alias destructeur) fait partie de la collecte des déchets (GC) - on ne sait pas quand (ou même si) cela se produit, car la GC se produit principalement en raison de la pression de la mémoire (c'est-à-dire le besoin de plus d'espace). Les finaliseurs ne sont généralement utilisés que pour nettoyer non géré puisque les ressources gérées auront leur propre collecte/élimination.

Par conséquent, IDisposable est utilisé pour de manière déterministe nettoyer les objets, c'est-à-dire maintenant. Il ne récupère pas la mémoire de l'objet (qui appartient toujours à GC) - mais il est utilisé par exemple pour fermer les fichiers, les connexions aux bases de données, etc.

Il y a beaucoup de sujets précédents sur ce sujet :

Enfin, notez qu'il n'est pas rare qu'une IDisposable pour avoir aussi un finalisateur ; dans ce cas, Dispose() appelle généralement GC.SuppressFinalize(this) Ce qui signifie que la GC n'exécute pas le finalisateur - elle se contente de jeter la mémoire (ce qui est beaucoup moins cher). Le finaliseur s'exécute quand même si vous oubliez de Dispose() l'objet.

0 votes

Merci ! C'est tout à fait logique. J'apprécie beaucoup cette réponse.

30 votes

Une chose supplémentaire à dire. N'ajoutez pas de finalizer à votre classe à moins que vous n'en ayez vraiment, vraiment besoin. Si vous ajoutez un finalizer (destructeur), la GC doit l'appeler (même un finalizer vide) et pour l'appeler, l'objet survivra toujours à un garbage collect de gen 1. Cela entrave et ralentit le GC. C'est pourquoi Marc dit d'appeler SuppressFinalize dans le code ci-dessus.

1 votes

Finalize consiste donc à libérer les ressources non gérées. Mais Dispose peut être utilisé pour libérer des ressources gérées et non gérées ?

28voto

Igal Tabachnik Points 15160

Le rôle de la Finalize() est de s'assurer qu'un objet .NET peut nettoyer les ressources non gérées. lors de la collecte des déchets . Cependant, les objets tels que les connexions aux bases de données ou les gestionnaires de fichiers doivent être libérés dès que possible, au lieu de compter sur la collecte des déchets. Pour cela, vous devez implémenter IDisposable et libérez vos ressources dans l'interface Dispose() méthode.

9voto

Jonathan Allen Points 23540

La seule chose qui devrait être dans un destructeur C# est cette ligne :

Dispose(False);

C'est ça. Rien d'autre ne devrait jamais se trouver dans cette méthode.

3 votes

Il s'agit du modèle de conception proposé par Microsoft dans la documentation .NET, mais ne l'utilisez pas lorsque votre objet n'est pas IDisposable. msdn.microsoft.com/fr/us/library/fs2xkftw%28v=vs.110%29.aspx

1 votes

Je ne vois aucune raison de proposer une classe avec un finaliseur qui n'ait pas aussi une méthode Dispose.

9voto

abatishchev Points 42425

Il existe une très bonne description sur MSDN :

L'utilisation principale de cette interface est à libérer les ressources non gérées . Le collecteur d'ordures automatiquement libère la mémoire alloué à un objet géré lorsque cet objet n'est plus plus utilisé. Cependant, il est pas possible de prédire quand la collecte la collecte des déchets se produira . En outre, le ramasseur d'ordures n'a pas connaissance des ressources non gérées comme les poignées de fenêtre, ou ouvrir fichiers et des ruisseaux.

Utilisez la méthode Dispose de cette interface pour explic explic explic explic explic explic explic explic des ressources non gérées en conjonction avec le ramasseur d'ordures. Le site consommateur d'un objet peut appeler cette méthode lorsque l'objet n'est plus nécessaire.

1 votes

L'une des principales faiblesses de cette description est que MS donne des exemples de ressources non gérées, mais, d'après ce que j'ai vu, ne définit jamais vraiment le terme. Puisque les objets gérés ne sont généralement utilisables que dans du code géré, on pourrait penser que les choses utilisées dans du code non géré sont des ressources non gérées, mais ce n'est pas vraiment vrai. Beaucoup de code non géré n'utilise pas de ressources, et certains types de ressources non gérées comme les événements n'existent que dans l'univers du code géré.

1 votes

Si un objet à courte durée de vie s'abonne à un événement d'un objet à longue durée de vie (par exemple, il demande à être informé de tout changement survenant pendant la durée de vie de l'objet à courte durée de vie), cet événement doit être considéré comme une ressource non gérée, car si l'on ne se désabonne pas de l'événement, la durée de vie de l'objet à courte durée de vie serait prolongée jusqu'à celle de l'objet à longue durée de vie. Si plusieurs milliers ou millions d'objets à courte durée de vie s'abonnent à un événement mais sont abandonnés sans être désabonnés, cela pourrait provoquer une fuite de mémoire ou de CPU (puisque le temps nécessaire pour traiter chaque abonnement augmenterait).

1 votes

Un autre scénario impliquant des ressources non gérées dans un code géré serait l'allocation d'objets à partir de pools. En particulier si le code doit s'exécuter dans le micro-cadre .NET (dont le ramasse-miettes est beaucoup moins efficace que celui des machines de bureau), il peut être utile pour le code de disposer, par exemple, d'un tableau de structures, dont chacune peut être marquée "utilisée" ou "libre". Une requête d'allocation doit trouver une structure qui est actuellement marquée "libre", la marquer "utilisée", et retourner un index vers elle ; une requête de libération doit marquer une structure comme "libre". Si une demande d'allocation renvoie par exemple 23, alors...

5voto

Brian Gideon Points 26683

Votre question concernant la nécessité ou non de toujours appeler Dispose est généralement un débat animé. Voir ce pour obtenir une perspective intéressante de la part de personnes respectées de la communauté .NET.

Personnellement, je pense que la position de Jeffrey Richter qui consiste à appeler Dispose n'est pas obligatoire est incroyablement faible. Il donne deux exemples pour justifier son opinion.

Dans le premier exemple, il dit appeler Dispose sur les contrôles Windows Forms est fastidieuse et inutile dans les scénarios courants. Cependant, il omet de mentionner que Dispose est en fait appelé automatiquement par les conteneurs de contrôle dans ces scénarios courants.

Dans le second exemple, il indique qu'un développeur peut supposer à tort que l'instance de IAsyncResult.WaitHandle devrait être éliminé de manière agressive sans se rendre compte que la propriété initialise paresseusement le gestionnaire d'attente, ce qui entraîne une pénalité de performance inutile. Mais, le problème avec cet exemple est que la propriété IAsyncResult n'est pas conforme aux directives publiées par Microsoft pour le traitement de l'information. IDisposable objets. C'est-à-dire que si une classe détient une référence à une IDisposable alors la classe elle-même doit implémenter IDisposable . Si IAsyncResult a suivi cette règle puis sa propre Dispose pourrait prendre la décision de savoir lequel de ses membres constitutifs doit être éliminé.

Donc, à moins que quelqu'un n'ait un argument plus convaincant, je vais rester dans le camp de "toujours appeler Dispose", tout en sachant qu'il y aura quelques cas marginaux qui découleront principalement de mauvais choix de conception.

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