107 votes

L’appel null sur une classe vs Dispose()

Je suis fasciné par la façon dont le CLR et GC fonctionne (je suis en train de travailler sur l'élargissement de mes connaissances sur cet par la lecture CLR via C#, Jon Skeet livres/posts, et plus).

De toute façon, quelle est la différence entre dire:

MyClass myclass = new MyClass();
myclass = null;

Ou, en faisant MyClass mettre en œuvre IDisposable et un destructeur et appelant Dispose()?

Aussi, si j'ai un bloc de code avec l'aide d'instruction (par exemple ci-dessous), si j'ai pas le code et à la sortie de l'aide du bloc, est l'objet éliminés ou lorsqu'un garbage collection se produit? Qu'arriverait-il si je Dispose() à l'aide du bloc anyay?

using (MyDisposableObj mydispobj = new MyDisposableObj())
{

}

Les classes de flux (par exemple BinaryWriter) ont une méthode Finalize? Pourquoi voudrais-je utiliser?

206voto

Jon Skeet Points 692016

Il est important de distinguer l'élimination de la collecte des ordures. Ils sont des choses totalement distinctes, avec un point commun sur lequel je vais venir dans une minute.

Dispose, la collecte des ordures et de la finalisation

Lorsque vous écrivez un using déclaration, c'est simplement du sucre syntaxique pour un try/finally bloc Dispose , même si le code dans le corps de l' using déclaration déclenche une exception. Cela ne veut pas dire que l'objet est d'ordures collectées à la fin du bloc.

L'élimination est sur des ressources non managées (non-ressources de la mémoire). Celles-ci pourraient être de l'INTERFACE utilisateur poignées, les connexions réseau, les descripteurs de fichiers etc. Ces ressources sont limitées, de sorte que vous devez généralement relâcher dès que vous le pouvez. Vous devez implémenter IDisposable chaque fois que votre type "propriétaire" d'une ressource non managée, soit directement (généralement par l'intermédiaire d'un IntPtr) ou indirectement (par exemple par un Stream, SqlConnection etc).

La collecte des ordures lui-même est seulement sur la mémoire - avec un petit twist. Le garbage collector est capable de trouver des objets qui ne sont plus référencés, et de les libérer. Il ne regarde pas de poubelle tout le temps, seulement quand il détecte qu'il doit (par exemple, si une "génération" des tas de manque de mémoire).

Le hic, c'est que la finalisation. Le garbage collector maintient une liste d'objets qui ne sont plus accessibles, mais qui ont un finaliseur (écrit comme ~Foo() en C#, un peu de prêter à confusion, - ils ne sont rien comme C++ destructeurs). Il exécute les finaliseurs sur ces objets, juste au cas où ils ont besoin de faire un nettoyage avant leur mémoire est libérée.

Les finaliseurs sont presque toujours utilisé pour nettoyer les ressources dans le cas où l'utilisateur du type a oublié d'en disposer d'une manière ordonnée. Donc, si vous ouvrez un FileStream mais oubliez pas d'appeler la Dispose ou Close, le finaliseur sera finalement libérer le fichier sous-jacent gérer pour vous. Dans un programme écrit, finaliseurs devrait presque jamais le feu à mon avis.

Définition d'une variable d' null

Un petit point sur la définition d'une variable d' null - ce n'est presque jamais nécessaire pour le bien de la collecte des ordures. Vous pouvez parfois avoir besoin de le faire si c'est une variable membre, bien que dans mon expérience, il est rare que le "cadre" d'un objet pour ne plus être nécessaire. Quand c'est une variable locale, le JIT est généralement assez intelligent (en mode release) pour savoir quand vous n'utilisez pas une référence à nouveau. Par exemple:

StringBuilder sb = new StringBuilder();
sb.Append("Foo");
string x = sb.ToString();

// The string and StringBuilder are already eligible
// for garbage collection here!
int y = 10;
DoSomething(y);

// These aren't helping at all!
x = null;
sb = null;

// Assume that x and sb aren't used here

Le seul moment où il peut être utile de définir une variable locale à l' null c'est quand vous êtes dans une boucle, et certaines branches de la boucle besoin d'utiliser la variable, mais vous savez que vous avez atteint un point où vous n'avez pas. Par exemple:

SomeObject foo = new SomeObject();

for (int i=0; i < 100000; i++)
{
    if (i == 5)
    {
        foo.DoSomething();
        // We're not going to need it again, but the JIT
        // wouldn't spot that
        foo = null;
    }
    else
    {
        // Some other code 
    }
}

La mise en œuvre de IDisposable/finaliseurs

Donc, si vos propres types de mettre en œuvre les finaliseurs? Presque certainement pas. Si vous avez seulement indirectement, détiennent des ressources non managées (par exemple, vous avez un FileStream comme une variable membre), puis l'ajout de votre propre finaliseur n'aide pas: le flux sera presque certainement être admissible pour la collecte des ordures lors de votre objet, de sorte que vous pouvez simplement appuyer sur FileStream ayant un finaliseur (si nécessaire, peut faire référence à autre chose, etc). Si vous souhaitez organiser une ressource non managée "presque" directement, SafeHandle est votre ami - il faut un peu de temps pour aller avec, mais cela signifie que vous aurez presque jamais besoin d'écrire un finaliseur de nouveau. Vous devez habituellement seulement besoin d'un finaliseur si vous avez un très directe sur la poignée de la ressource ( IntPtr) et vous devriez regarder pour se déplacer à l' SafeHandle dès que vous le pouvez. (Il y a deux liens, il y lire à la fois, dans l'idéal.)

Joe Duffy a une très longue série de lignes directrices sur les finaliseurs et IDisposable (co-écrit avec beaucoup de smart folk) qui sont la peine de lire. Il vaut la peine d'être conscient que si vous scellez vos classes, il rend la vie beaucoup plus facile: le motif de l'annulation de la Dispose d'un appel à une nouvelle virtuel Dispose(bool) méthode etc n'est utile que si votre classe est conçu pour l'héritage.

Cela a été un peu me promener, mais s'il vous plaît demander des éclaircissements où vous le souhaitez :)

22voto

recursive Points 34729

Lorsque vous disposez d’un objet, les ressources sont libérées. Lorsque vous affectez la valeur null à une variable, vous êtes simplement en changeant une référence.

Après avoir exécuté ce, l’objet myclass faisait allusion aux fixes existe et continuera jusqu'à ce que le GC se déplace à il nettoyage. Si Dispose est appelé explicitement, ou c’est un aide bloc, toutes les ressources seront libérées dès que possible.

6voto

jalf Points 142628

Les deux opérations n'ont pas beaucoup à faire avec les uns des autres. Lorsque vous définissez une référence à null, il ne suffit que. Il n'a pas en soi d'effet de la classe qui a été fait référence à tous. Votre variable tout simplement plus de points à l'objet, mais l'objet lui-même reste inchangé.

Lorsque vous appelez Dispose(), c'est un appel de méthode sur l'objet lui-même. Quelle que soit la méthode dispose, est maintenant effectuée sur l'objet. Mais cela n'affecte pas votre référence à l'objet.

La seule zone de chevauchement est que quand il n'y a pas plus de références à un objet, il sera éventuellement obtenir des ordures collectées. Et si la classe implémente l'interface IDisposable, puis les Disposer() sera appelée sur l'objet avant qu'il ne se ramasse miettes.

Mais cela n'arrivera pas immédiatement après la configuration de votre référence à null, et ce pour deux raisons. Tout d'abord, d'autres références peuvent exister, donc il ne sera pas nettoyée à tous encore, et deuxièmement, même si c'était la dernière de référence, de sorte qu'il est maintenant prêt à être des ordures collectées, rien ne se passera jusqu'à ce que le garbage collector décide de supprimer l'objet.

Appelant dispose() sur un objet de ne pas "tuer" l'objet d'une quelconque façon. Il est couramment utilisé pour nettoyer de sorte que l'objet peut être supprimé en toute sécurité par la suite, mais en fin de compte, il n'y a rien de magique en Disposer, c'est juste une méthode de classe.

-1voto

Erik Funkenbusch Points 53436

Lorsque vous affectez la valeur null à une variable, il finira par appeler dispose elle-même, mais lorsqu'il le fait, c'est à la bibliothèque d'exécution. Jetez vous donne la possibilité de la conception déterministe de la gratuit de ressources, même si l'objet lui-même peut ne pas être détruit.

Considérez aussi qu'un objet peut avoir plus d'une référence à elle, mais vous pouvez toujours jeter (pas une bonne pratique, mais il y a des fois, il pourrait être approprié). Cela vous permet de détruire les ressources, tandis que l'objet est toujours référencé.

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