En C # quelle est la différence entre un destructeur et une méthode Finalize dans une classe?

97

Quelle est la différence, s'il y en a une, entre un destructeur et une méthode Finalize dans une classe?

J'ai récemment découvert que Visual Studio 2008 considère un destructeur comme synonyme d'une méthode Finalize, ce qui signifie que Visual Studio ne vous permettra pas de définir simultanément les deux méthodes dans une classe.

Par exemple, le fragment de code suivant:

class TestFinalize
{
    ~TestFinalize()
    {
        Finalize();
    }

    public bool Finalize()
    {
        return true;
    }
}

Donne l'erreur suivante sur l'appel à Finalize dans le destructeur:

L'appel est ambigu entre les méthodes ou propriétés suivantes: 'TestFinalize. ~ TestFinalize ()' et 'TestFinalize.Finalize ()'

Et si l'appel à Finalize est commenté, cela donne l'erreur suivante:

Le type 'ManagementConcepts.Service.TestFinalize' définit déjà un membre appelé 'Finalize' avec les mêmes types de paramètres

Jeff Leonard
la source

Réponses:

68

Un destructeur en C # remplace la System.Object.Finalizeméthode. Vous devez utiliser la syntaxe du destructeur pour ce faire. Le remplacement manuel Finalizevous donnera un message d'erreur.

En gros, ce que vous essayez de faire avec votre Finalizedéclaration de méthode est de masquer la méthode de la classe de base. Cela amènera le compilateur à émettre un avertissement qui peut être réduit au silence en utilisant le newmodificateur (s'il allait fonctionner). La chose importante à noter ici est que vous ne pouvez pas les deux overrideet déclarer un newmembre avec le même nom en même temps, donc avoir à la fois un destructeur et une Finalizeméthode entraînera une erreur (mais vous pouvez , bien que ce ne soit pas recommandé, déclarer une public new void Finalize()méthode si vous ne déclarez pas un destructeur).

Mehrdad Afshari
la source
71

Wikipedia a une bonne discussion sur la différence entre un finaliseur et un destructeur dans l' article du finaliseur .

C # n'a vraiment pas de "vrai" destructeur. La syntaxe ressemble à un destructeur C ++, mais c'est vraiment un finaliseur. Vous l'avez écrit correctement dans la première partie de votre exemple:

~ClassName() { }

Ce qui précède est un sucre syntaxique pour une Finalizefonction. Il garantit que les finaliseurs de la base sont garantis de s'exécuter, mais est par ailleurs identique au remplacement de la Finalizefonction. Cela signifie que lorsque vous écrivez la syntaxe du destructeur, vous écrivez vraiment le finaliseur.

Selon Microsoft , le finaliseur fait référence à la fonction que le garbage collector appelle quand il collecte ( Finalize), tandis que le destructeur est votre morceau de code qui s'exécute en conséquence (le sucre syntaxique qui devient Finalize). Ils sont si proches de la même chose que Microsoft n'aurait jamais dû faire la distinction.

L'utilisation par Microsoft du terme «destructeur» du C ++ est trompeuse, car en C ++, il est exécuté sur le même thread dès que l'objet est supprimé ou sorti de la pile, tandis qu'en C #, il est exécuté sur un thread distinct à un autre moment.

Kenzi
la source
Je dirais qu'une telle distinction entre le destructeur et le finaliseur est importante à faire. Cependant, seuls ceux qui se soucient de ce qui se passe sous le capot se soucieraient de cette distinction.
Kyle Baran
1
Notez également que l'ECMA-334 a explicitement levé l'ambiguïté du «destructeur» et du «finaliseur» il y a longtemps. Je ne sais pas pourquoi MS insiste encore sur le terme trompeur dans leurs spécifications.
FrankHB
Au moins à partir de l'utilisation de Mono, C # est en fait modelé sur C ++, et la plupart des objets C # natifs sont des objets C ++. La façon dont le compilateur qui a compilé Mono fonctionne dicte comment ces objets C ++ sont détruits, et de même, comment la finalisation des objets C # se propage vers C ++ et appelle ces destructeurs. La distinction a du sens sous le capot, mais elle ne s'applique toujours pas vraiment à C # lui-même.
Kenzi
20

Trouvé ici: http://sanjaysainitech.blogspot.com/2007/06/difference-between-destructor-dispose.html

  1. Destructeur

    Ce sont des méthodes spéciales qui contiennent du code de nettoyage pour l'objet. Vous ne pouvez pas les appeler explicitement dans votre code car ils sont appelés implicitement par GC. En C #, ils ont le même nom que le nom de la classe précédé du ~signe. Comme-

    Class MyClass
    {
    
    ~MyClass()
    {
    .....
    }
    }

    Dans VB.NET, les destructeurs sont implémentés en remplaçant la méthode Finalize de la classe System.Object.

  2. Disposer

    Celles-ci sont comme toutes les autres méthodes de la classe et peuvent être appelées explicitement, mais elles ont un but spécial de nettoyage de l'objet. Dans la méthode dispose, nous écrivons du code de nettoyage pour l'objet. Il est important que nous libéraions toutes les ressources non gérées dans la méthode dispose, comme la connexion à la base de données, les fichiers, etc. La classe implémentant la méthode dispose doit implémenter l'interface IDisposable. class a desturctor car il a déjà effectué le travail de nettoyage de l'objet, il n'est donc pas nécessaire que le garbage collector appelle la méthode Finalize de l'objet. Référence: http://msdn2.microsoft.com/en-us/library/aa720161(VS.71).aspx

  3. Finaliser

    Une méthode Finalize agit comme une sauvegarde pour nettoyer les ressources dans le cas où votre méthode Dispose n'est pas appelée. Vous ne devez implémenter une méthode Finalize que pour nettoyer les ressources non gérées. Vous ne devez pas implémenter une méthode Finalize pour les objets gérés, car le garbage collector nettoie automatiquement les ressources gérées. La méthode Finalize est appelée implicitement par le GC, vous ne pouvez donc pas l'appeler à partir de votre code.

    Remarque: En C #, la méthode Finalize ne peut pas être remplacée, vous devez donc utiliser un destructeur dont l'implémentation interne remplacera la méthode Finalize dans MSIL.Mais dans VB.NET, la méthode Finalize peut être remplacée car elle prend en charge la méthode destructor.

Mise à jour: Sujet semi-lié intéressant ici .

Andrew Siemer
la source
1
You should only implement a Finalize method to clean up unmanaged resources: vous le mettez dans Finalize. Pareil avec Dispose?
hqt
@hqt: Les cas où l'on devrait implémenter Disposebeaucoup plus que ceux où l'on devrait implémenter un finaliseur. Implémenter Disposes'il est probable qu'une instance de la classe ou d'une classe dérivée sera la dernière chose à posséder directement une ressource non gérée, ou à posséder directement la dernière chose à posséder directement une ressource non gérée, ou à posséder directement la dernière chose à posséder directement etc. FinalizeN'implémentez le nettoyage des ressources que si la classe <i> directement </i> possède une ressource non gérée <i> et presque rien d'autre </i> - un scénario beaucoup plus étroit.
supercat du
@hqt: Si une classe possède directement des ressources non managées et contient également des références à d'autres objets, les ressources non managées devraient généralement être divisées en leur propre classe finalisable (qui ne devrait idéalement pas contenir de références fortes à autre chose), ce qui signifie la classe qui contient des références à d'autres objets ne posséderait que des «choses qui possèdent directement des ressources non gérées», plutôt que de posséder les ressources elles-mêmes, et n'aurait donc pas besoin d'un finaliseur.
supercat du