Destructeur vs IDisposable?

J’ai lu à propos de l’élimination des objects / de l’interface IDisposable et des destructeurs dans C #, mais pour moi, ils semblent faire la même chose?

Quelle est la différence entre les deux? Pourquoi devrais-je utiliser l’un sur l’autre? En fait, dans cet exemple (lien ci-dessous), ce code utilise à la fois l’interface IDisposable et un destructeur:

http://msdn.microsoft.com/en-us/library/system.idisposable.aspx

Le commentaire indique que le destructeur est si le code de finalisation n’est pas utilisé, mais comment puis-je décider quand utiliser l’un sur l’autre?

J’ai écrit un article assez détaillé qui devrait aider à expliquer les finaliseurs, IDisposable, et quand vous devriez utiliser l’un ou l’autre: http://gregbee.ch/blog/implementing-and-using-the-idisposable-interface

La partie la plus pertinente est probablement citée ci-dessous:

Lorsque vous utilisez des ressources non gérées, telles que des descripteurs et des connexions à des bases de données, vous devez vous assurer qu’elles sont conservées pendant une durée minimale, en utilisant le principe de l’acquisition tardive et de la publication anticipée. En C ++, la libération des ressources se fait généralement dans le destructeur, qui est exécuté de manière déterministe au point où l’object est supprimé. Le runtime .NET, cependant, utilise un récupérateur de place (GC) pour nettoyer et récupérer la mémoire utilisée par les objects qui ne sont plus accessibles; comme cela fonctionne de manière périodique, cela signifie que le point auquel votre object est nettoyé est non déterministe. La conséquence en est que les destructeurs n’existent pas pour les objects gérés car il n’y a pas de lieu déterministe pour les exécuter.

Au lieu de destructeurs, C # a des finaliseurs qui sont implémentés en remplaçant la méthode Finalize définie dans la classe Object de base (bien que C # utilise quelque peu la syntaxe du destructeur C ++ ~ Object pour cela). Si un object remplace la méthode Finalize, plutôt que d’être collecté par le GC lorsqu’il est hors de scope, le GC le place dans une queue du finaliseur. Lors du cycle suivant du GC, tous les finaliseurs de la queue sont exécutés (sur un seul thread dans l’implémentation actuelle) et la mémoire des objects finalisés est récupérée. C’est assez évident pour cela que vous ne voulez pas faire de nettoyage dans un finaliseur: il faut deux cycles de GC pour collecter l’object au lieu d’un et il y a un seul thread où tous les finaliseurs sont exécutés pendant que chaque autre thread est suspendu. ça va nuire à la performance.

Donc, si vous n’avez pas de destructeurs et que vous ne voulez pas laisser le nettoyage au finaliseur, la seule option consiste à nettoyer manuellement, de manière déterministe, l’object. Entrez l’interface IDisposable qui fournit une norme pour prendre en charge cette fonctionnalité et définit une méthode unique, Dispose, dans laquelle vous placez la logique de nettoyage pour l’object. Lorsqu’elle est utilisée dans un bloc finally, cette interface fournit des fonctionnalités équivalentes aux destructeurs. La raison pour laquelle enfin des blocs dans le code est principalement de prendre en charge l’interface IDisposable; c’est pourquoi C ++ utilise simplement essayer / sauf qu’il n’y a pas besoin de bloc final avec des destructeurs.

Version courte

Un finaliseur vous donne la possibilité de disposer de ressources non managées au cas où l’utilisateur de votre object aurait oublié d’appeler IDisposable.Dispose .

Si votre object implémente IDisposable , l’utilisateur de votre object doit appeler .Dispose . Vous n’avez pas à nettoyer le mess de l’utilisateur. mais c’est une bonne chose à faire.


Ma réponse la plus populaire sur Stackoverflow vous explique depuis le début pourquoi vous avez IDisposable, ce qu’il doit faire, ce que votre finalizer peut faire, ce qu’il ne doit pas faire.

Cette réponse fait fondre les visages

a été utilisé pour le décrire: P

Avoir un destructeur (~ Object ()) en langage de programmation géré est l’idée la plus fausse. Il est parfaitement logique que les langages non gérés comme C, C ++ aient des destructeurs car ils utilisent l’idiome RAII, mais pour Java, C #, c’est tellement absurde.

Joshua Bloch, ancien responsable du projet Java Collection Framework, a souligné que l’idée de la méthode finalize () (équivalente au destructeur C ++ de Java) dans Java était la plus grande erreur jamais faite. Identique à C #, finallize () dans Java donne une surcharge à “new” car il doit être ajouté à la queue du finallizer lors de l’allocation. De plus, Garbage Collector doit apparaître et exécuter finallize () dans la queue, soit deux fois plus de temps que gc.

C # possédait de nombreuses fonctionnalités améliorées, telles que “(IDisposable) {}”, qui permet non seulement de limiter la variable IDisposable à la scope du bloc “using”, mais également de garantir son nettoyage. Ma question est la suivante: pourquoi C # a-t-il suivi la même piste de Java, ce qui a entraîné de grandes erreurs? Peut-être si le développement de dotnet a commencé après 2003 ~ 2005 environ, quand de nombreux architectes Java ont trouvé l’erreur de finallize (), alors l’erreur aurait été évitée.

Beaucoup de bonnes idées d’un langage sont souvent transférées dans un autre langage, comme le “combo IDisposable / using” en C # qui a été transféré dans Java 1.7 dans son instruction “try (object-to-dispose) {}”. Mais il est dommage que les architectes linguistiques ne parviennent pas à découvrir la mauvaise idée déguisée en bonne idée lors de son transfert de l’un à l’autre.

Mon conseil est de ne jamais utiliser ~ Destructor () et de conserver le combo IDisposable / using si vous avez besoin de nettoyer manuellement la ressource non gérée comme les connexions à la firebase database.