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 :)