Quelle est la différence entre utiliser IDisposable et un destructeur en C #?
-
19-08-2019 - |
Question
Quand devrais-je implémenter IDispose sur une classe plutôt que sur un destructeur? Je lis cet article , mais je manque toujours le point .
Mon hypothèse est que si j'implémente IDispose sur un objet, je peux explicitement le "détruire", par opposition à l'attente du garbage collector. Est-ce correct?
Est-ce que cela signifie que je devrais toujours appeler explicitement Dispose sur un objet? Quels sont quelques exemples courants de cela?
La solution
Un finaliseur (ou destructeur) fait partie de la récupération de place (GC). Il est indéterminé quand (ou même si) cela se produit, car la GC se produit principalement à la suite d’une pression de la mémoire (c’est-à-dire qu’elle nécessite plus d’espace). Les finaliseurs ne sont généralement utilisés que pour nettoyer les ressources non gérées , car les ressources gérées auront leur propre collecte / élimination.
Par conséquent, IDisposable
est utilisé pour nettoyer de manière déterministe , c’est-à-dire maintenant. Il ne collecte pas la mémoire de l'objet (qui appartient toujours à GC) - mais sert par exemple à fermer des fichiers, des connexions à une base de données, etc.
Il existe de nombreux sujets précédents sur ce sujet:
Enfin, notez qu'il n'est pas rare qu'un objet IDisposable
ait également un finaliseur; Dans ce cas, Dispose ()
appelle généralement GC.SuppressFinalize (this)
, ce qui signifie que GC n'exécute pas le finaliseur. Il jette simplement la mémoire (beaucoup moins chère). . Le finaliseur continue de fonctionner si vous oubliez Dispose ()
de l'objet.
Autres conseils
Le rôle de la méthode Finalize ()
est de garantir qu'un objet .NET peut nettoyer les ressources non gérées lors de la récupération des ordures . Toutefois, les objets tels que les connexions à la base de données ou les gestionnaires de fichiers doivent être libérés dès que possible, au lieu de compter sur le garbage collection. Pour cela, vous devez implémenter l'interface IDisposable
et libérer vos ressources dans la méthode Dispose ()
.
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 ramasse-miettes automatiquement libère la mémoire allouée à un objet géré lorsque cet objet est non plus utilisé. Cependant, ce n'est pas possible de prédire quand les ordures la collecte aura lieu . En outre, le ramasse-miettes n'a pas connaissance des ressources non gérées comme les poignées de fenêtre ou ouvert fichiers et les flux.
Utilisez la méthode Dispose de cette interface pour publier explicitement ressources non gérées conjointement avec le ramasse-miettes. le consommateur d'un objet peut appeler cette méthode lorsque l'objet n'est pas besoin plus longtemps.
La seule chose qui devrait figurer dans un destructeur C # est cette ligne:
Dispose(False);
C'est ça. Rien d’autre ne devrait être dans cette méthode.
Votre question concernant le fait de toujours appeler Éliminer
est généralement un débat houleux. Voir ce blog pour une perspective intéressante émanant de personnes respectées de la communauté .NET.
Personnellement, je pense que la position de Jeffrey Richter selon laquelle appeler Dispose
n'est pas obligatoire est incroyablement faible. Il donne deux exemples pour justifier son opinion.
Dans le premier exemple, il déclare qu'appeler Dispose
sur les contrôles Windows Forms est fastidieux et inutile dans les scénarios classiques. Cependant, il omet de mentionner que Dispose
est en fait appelé automatiquement par les conteneurs de contrôle dans ces scénarios classiques.
Dans le deuxième exemple, il indique qu'un développeur peut supposer à tort que l'instance de IAsyncResult.WaitHandle
doit être supprimée de manière agressive sans que la propriété initialise paresseusement le descripteur d'attente, ce qui entraîne une pénalité de performance inutile. Cependant, le problème de cet exemple est que le IAsyncResult
n’adhère pas aux directives publiées par Microsoft concernant le traitement des objets IDisposable
. C'est-à-dire que si une classe contient une référence à un type IDisposable
, la classe elle-même devrait implémenter IDisposable
. Si IAsyncResult
a suivi cette règle, sa propre méthode Dispose
peut prendre la décision concernant les membres qui doivent être éliminés.
Donc, à moins que quelqu'un ait un argument plus convaincant, je vais rester dans le "toujours appeler Dispose". camp avec la compréhension qu'il va y avoir quelques cas marginaux qui découlent principalement de mauvais choix de conception.
C'est très simple, vraiment. Je sais que la réponse a été donnée, mais je vais réessayer, mais je vais essayer de garder les choses aussi simples que possible.
Un destructeur ne devrait généralement jamais être utilisé. Il est seulement exécuté. Net veut le faire fonctionner. Il ne fonctionnera qu'après un cycle de collecte de déchets. Il se peut qu’il ne soit jamais exécuté pendant le cycle de vie de votre application. Pour cette raison, vous ne devriez jamais mettre de code dans un destructeur qui doit être exécuté. Vous ne pouvez pas non plus vous fier à l'existence d'objets existant dans la classe lors de son exécution (ils ont peut-être déjà été nettoyés, l'ordre dans lequel les destructeurs étant exécutés n'étant pas garanti).
IDisposible doit être utilisé chaque fois que vous avez un objet qui crée des ressources qui nécessitent un nettoyage (par exemple, des descripteurs de fichiers et graphiques). En fait, nombreux sont ceux qui soutiennent que tout ce que vous mettez dans un destructeur doit être inséré de manière indissociable pour les raisons susmentionnées.
La plupart des classes appelleront disposer lorsque le finaliseur sera exécuté, mais il s’agit simplement d’une sécurité et ne doit jamais être invoqué. Vous devez explicitement jeter tout ce qui implémente IDisposable lorsque vous avez terminé. Si vous implémentez IDisposable, vous devez appeler dispose dans le finaliseur. Voir http://msdn.microsoft.com/en-us/library /system.idisposable.aspx pour un exemple.
Voici un autre excellent article qui élimine une partie de la brume entourant IDisposable, le GC et l’élimination.