Quand dois-je créer un destructeur?

185

Par exemple:

public class Person
{
    public Person()
    {
    }

    ~Person()
    {
    }
}

Quand dois-je créer manuellement un destructeur? Quand avez-vous eu besoin de créer un destructeur?

David Heffernan
la source
1
Le langage C # appelle ces «destructeurs», mais la plupart des gens les appellent «finaliseurs» car c'est leur nom .NET et cela réduit la confusion avec les destructeurs C ++ (qui sont assez différents). Comment implémenter IDisposable et Finalizers: 3 règles faciles
Stephen Cleary
5
Lorsque vous vous sentez imprudent.
capdragon le
32
Je pense que le clavier de TomTom fonctionnait mal. Le verrouillage des majuscules passait et s'éteignait sporadiquement. Bizarre.
Jeff LaFay le
voir aussi stackoverflow.com/questions/1076965/…
Ian Ringrose
J'ai fini par utiliser un destructeur comme aide au débogage basée sur la suggestion de Greg Beech: stackoverflow.com/questions/3832911/…
Brian

Réponses:

237

MISE À JOUR: Cette question a fait l'objet de mon blog en mai 2015 . Merci pour la bonne question! Consultez le blog pour une longue liste de mensonges que les gens croient généralement à propos de la finalisation.

Quand dois-je créer manuellement un destructeur?

Presque jamais.

En règle générale, on ne crée un destructeur que lorsque votre classe conserve une ressource non gérée coûteuse qui doit être nettoyée lorsque l'objet disparaît. Il est préférable d'utiliser le modèle jetable pour s'assurer que la ressource est nettoyée. Un destructeur est alors essentiellement une assurance que si le consommateur de votre objet oublie de le supprimer, la ressource finit toujours par être nettoyée. (Peut être.)

Si vous créez un destructeur, soyez extrêmement prudent et comprenez comment fonctionne le garbage collector . Les destructeurs sont vraiment bizarres :

  • Ils ne fonctionnent pas sur votre fil; ils fonctionnent sur leur propre fil. Ne causez pas de blocages!
  • Une exception non gérée lancée par un destructeur est une mauvaise nouvelle. C'est sur son propre fil; qui va l'attraper?
  • Un destructeur peut être appelé sur un objet après le démarrage du constructeur mais avant la du constructeur. Un destructeur correctement écrit ne dépendra pas des invariants établis dans le constructeur.
  • Un destructeur peut «ressusciter» un objet, rendant à nouveau un objet mort vivant. C'est vraiment bizarre. Ne fais pas ça.
  • Un destructeur pourrait ne jamais fonctionner; vous ne pouvez pas vous fier à la planification de la finalisation de l'objet. Ce sera probablement le cas, mais ce n'est pas une garantie.

Presque rien de ce qui est normalement vrai n'est vrai dans un destructeur. Soyez vraiment très prudent. Ecrire un destructeur correct est très difficile.

Quand avez-vous eu besoin de créer un destructeur?

Lors du test de la partie du compilateur qui gère les destructeurs. Je n'ai jamais eu besoin de le faire dans le code de production. J'écris rarement des objets qui manipulent des ressources non gérées.

Eric Lippert
la source
"Un destructeur peut être appelé sur un objet après le démarrage du constructeur mais avant la fin du constructeur." Mais je peux compter sur les initialiseurs de champ pour s'exécuter, non?
configurateur
13
@configurator: Non. Supposons le troisième initialiseur de champ d'un objet avec un finaliseur appelé méthode statique qui a provoqué la levée d'une exception. Quand le quatrième initialiseur de champ serait-il exécuté? Jamais. Mais l'objet est toujours alloué et doit être finalisé. Heck, vous n'avez même pas la garantie que les champs de type double ont été entièrement initialisés lorsque le dtor s'exécute. Il aurait pu y avoir un thread abandonné à mi-chemin de l'écriture du double et maintenant le finaliseur doit gérer un double demi-zéro demi-initialisé.
Eric Lippert
1
Excellent article, mais j'aurais dû dire "devrait être créé lorsque votre classe tient un objet coûteux non managé ou provoque l'existence d'un grand nombre d'objets non gérés" - Pour un exemple concret, j'ai une classe de matrice en C # qui utilise un C ++ natif sous-jacent matrice pour faire beaucoup de travail lourd - je fais beaucoup de matrices - un "destructor" est bien supérieur à IDisposable dans ce cas précis, car il maintient les côtés gérés et non gérés de la maison mieux synchronisés
Mark Mullin
1
pythonnet utilise un destructeur pour libérer GIL en CPython non
managé
3
Article génial Eric. Props for this -> "Bonus supplémentaire amusant: le runtime utilise une génération de code moins agressive et un garbage collection moins agressif lors de l'exécution du programme dans le débogueur, car c'est une mauvaise expérience de débogage que les objets que vous déboguez disparaissent soudainement même si le la variable faisant référence à l'objet est dans la portée. Cela signifie que si vous avez un bogue où un objet est finalisé trop tôt, vous ne pourrez probablement pas reproduire ce bogue dans le débogueur! "
Ken Palmer
17

C'est ce qu'on appelle un "finaliseur", et vous ne devriez généralement en créer qu'un pour une classe dont l'état (ie: champs) inclut des ressources non managées (c'est-à-dire: des pointeurs vers des descripteurs récupérés via des appels p / invoke). Cependant, dans .NET 2.0 et versions ultérieures, il existe en fait un meilleur moyen de gérer le nettoyage des ressources non gérées: SafeHandle . Compte tenu de cela, vous ne devriez pratiquement plus jamais avoir besoin d'écrire un finaliseur.

Nicole Calinoiu
la source
25
@ThomasEding - Oui, ça l'est . C # utilise la syntaxe du destructeur, mais il crée en fait un finaliseur . Encore une fois .
JDB se souvient encore de Monica
@JDB: La construction linguistique est appelée un destructeur. Je n'aime pas le nom, mais c'est comme ça qu'il s'appelle. Le fait de déclarer un destructeur amène le compilateur à générer une méthode de finaliseur qui contient un peu de code wrapper avec tout ce qui apparaît dans le corps du destructeur.
supercat
8

Vous n'en avez pas besoin, sauf si votre classe gère des ressources non gérées comme les descripteurs de fichiers Windows.

John Saunders
la source
5
Eh bien, en fait, ça s'appelle un destructeur
David Heffernan
2
Maintenant, je suis confus. Est-ce un finaliseur ou un destructeur?
4
La spécification C # l'appelle en fait un destructeur. Certains voient cela comme une erreur. stackoverflow.com/questions/1872700/…
Ani
2
@ThomasEding - Oui, ça l'est . C # utilise la syntaxe du destructeur, mais il crée en fait un finaliseur .
JDB se souvient encore de Monica
2
J'adore les commentaires ici, vrai panto :)
Benjol
4

Il s'appelle un destructeur / finaliseur et est généralement créé lors de l'implémentation du modèle Disposed.

C'est une solution de secours lorsque l'utilisateur de votre classe oublie d'appeler Dispose, pour s'assurer que (éventuellement) vos ressources sont libérées, mais vous n'avez aucune garantie quant au moment où le destructeur est appelé.

Dans cette question de Stack Overflow , la réponse acceptée montre correctement comment implémenter le modèle d'élimination. Ceci n'est nécessaire que si votre classe contient des ressources non gérées que le garbage collector ne parvient pas à nettoyer lui-même.

Une bonne pratique consiste à ne pas implémenter de finaliseur sans donner également à l'utilisateur de la classe la possibilité de supprimer manuellement l'objet pour libérer les ressources immédiatement.

Øyvind Bråthen
la source
En fait, il n'est PAS appelé un destructeur en C # pour une bonne raison.
TomTom le
14
En fait, ça l'est . Merci de me donner un vote défavorable parce que vous vous trompez. Consultez la bibliothèque MSDN concernant ce problème spécifique: msdn.microsoft.com/en-us/library/66x5fx1b.aspx
Øyvind Bråthen
1
@TomTom son nom officiel est destructeur
David Heffernan
Ce n'est en fait pas une méthode de secours, cela permet simplement au GC de gérer le moment où vos objets libèrent des ressources non gérées, l'implémentation d'IDisposable vous permet de gérer cela vous-même.
HasaniH
3

Lorsque vous avez des ressources non gérées et que vous devez vous assurer qu'elles seront nettoyées lorsque votre objet disparaîtra. Un bon exemple serait les objets COM ou les gestionnaires de fichiers.

Vlad Bezden
la source
2

J'ai utilisé un destructeur (à des fins de débogage uniquement) pour voir si un objet était purgé de la mémoire dans le cadre d'une application WPF. Je ne savais pas si le garbage collection purgeait vraiment l'objet de la mémoire, et c'était un bon moyen de vérifier.

Neil.Allen
la source
1

Les destructeurs fournissent un moyen implicite de libérer des ressources non managées encapsulées dans votre classe, ils sont appelés lorsque le GC s'y déplace et ils appellent implicitement la méthode Finalize de la classe de base. Si vous utilisez beaucoup de ressources non gérées, il est préférable de fournir un moyen explicite de libérer ces ressources via l'interface IDisposable. Consultez le guide de programmation C #: http://msdn.microsoft.com/en-us/library/66x5fx1b.aspx

HasaniH
la source