Avez-vous besoin d'éliminer des objets et les mettre à null, ou le garbage collector propre lorsqu'ils sont hors de portée?
Réponses
Trop de publicités?Les objets seront nettoyés lorsqu'ils ne sont plus utilisés et lorsque le garbage collector, le juge approprié. Parfois, vous devrez peut-être définir un objet de null
, afin de le faire sortir du champ d'application (comme un champ statique dont la valeur, vous n'avez plus besoin), mais globalement, il n'est généralement pas nécessaire de régler à l' null
.
Concernant l'élimination des objets, je suis d'accord avec @Andre. Si l'objet est - IDisposable
c'est une bonne idée d'en disposer quand vous n'en avez plus besoin, en particulier si l'objet utilise des ressources non managées. Ne pas disposer des ressources non managées, va conduire à des fuites de mémoire.
Vous pouvez utiliser l' using
déclaration automatiquement disposer d'un objet une fois que votre programme laisse le champ d'application de l' using
déclaration.
using (MyIDisposableObject obj = new MyIDisposableObject())
{
// use the object here
} // the object is disposed of here
Qui est fonctionnellement équivalent à:
MyIDisposableObject obj;
try
{
obj = new MyIDisposableObject();
}
finally
{
if (obj != null)
{
((IDisposable)obj).Dispose();
}
}
Les objets ne sortent jamais du champ d'application en C# comme en C++. Ils sont traités par le Garbage Collector automatiquement lorsqu'ils ne sont plus utilisés. C'est un plus compliqué que C++ où la portée d'une variable est entièrement déterministe. CLR garbage collector va activement par le biais de tous les objets qui ont été créés et si ils sont utilisés.
Un objet peut aller "hors champ" dans une fonction mais si sa valeur est renvoyée, puis GC serait de regarder si oui ou non la fonction d'appel est titulaire sur la valeur de retour.
L'objet de références à null
est inutile que la collecte des ordures œuvres de travail quels objets sont référencés par d'autres objets.
Dans la pratique, vous n'avez pas à vous soucier de la destruction, ça marche et c'est génial :)
Dispose
doit être appelée sur tous les objets qui implémentent IDisposable
lorsque vous avez terminé de travailler avec eux. Normalement vous devriez utiliser un using
bloc avec les objets de la sorte:
using (var ms = new MemoryStream()) {
//...
}
EDIT Sur la portée des variables. Craig a demandé si l'étendue de la variable a un effet sur la durée de vie des objets. Pour bien expliquer cet aspect de la CLR, je vais avoir besoin d'expliquer quelques notions de C++ et C#.
Réelle portée des variables
Dans les deux langues, la variable ne peut être utilisé dans la même portée qu'il a été défini de la classe, une fonction ou un bloc entouré par des accolades. La subtile différence, cependant, est que les variables en C# ne peut pas être redéfini dans un bloc imbriqué.
En C++, c'est parfaitement légal:
int iVal = 8;
//iVal == 8
if (iVal == 8){
int iVal = 5;
//iVal == 5
}
//iVal == 8
En C#, cependant, vous obtenez une erreur du compilateur:
int iVal = 8;
if(iVal == 8) {
int iVal = 5; //error CS0136: A local variable named 'iVal' cannot be declared in this scope because it would give a different meaning to 'iVal', which is already used in a 'parent or current' scope to denote something else
}
Ce a de sens que si vous regardez généré MSIL - toutes les variables utilisées par la fonction sont définies au début de la fonction. Jetez un oeil à cette fonction:
public static void Scope() {
int iVal = 8;
if(iVal == 8) {
int iVal2 = 5;
}
}
Ci-dessous est généré IL. Notez que iVal2, qui est définie à l'intérieur de la si le bloc est en fait défini au niveau de la fonction. Effectivement, cela signifie que le C# n'a la classe et le niveau de fonctionnement de la portée de la mesure de la variable de durée de vie.
.method public hidebysig static void Scope() cil managed
{
// Code size 19 (0x13)
.maxstack 2
.locals init ([0] int32 iVal,
[1] int32 iVal2,
[2] bool CS$4$0000)
//Function IL - omitted
} // end of method Test2::Scope
C++ portée et la durée de vie des objets
Chaque fois qu'un C++ variable, alloué sur la pile, est hors de portée, il obtient détruits. Rappelez-vous qu'en C++, vous pouvez créer des objets sur la pile ou sur le tas. Lorsque vous créez sur la pile, une fois l'exécution laisse le champ, ils se sont détachés de la pile et est détruit.
if (true) {
MyClass stackObj; //created on the stack
MyClass heapObj = new MyClass(); //created on the heap
obj.doSomething();
} //<-- stackObj is destroyed
//heapObj still lives
Lorsque des objets en C++ sont créés sur le tas, ils doivent être explicitement détruit, sinon c'est une fuite de mémoire. Pas ce problème avec les variables de pile.
C# Durée De Vie Des Objets
Dans le CLR, des objets (c'est à dire les types de référence) sont toujours créé sur le tas managé. Cela est encore renforcé par la création d'objet de syntaxe. Considérons cet extrait de code.
MyClass stackObj;
En C++, ce serait de créer une instance d' MyClass
sur la pile et l'appel de son constructeur par défaut. En C#, il serait de créer une référence à la classe MyClass
qui ne pointent pas vers quoi que ce soit. La seule façon de créer une instance d'une classe est par l'utilisation de new
opérateur:
MyClass stackObj = new MyClass();
Dans un sens, C# les objets sont un peu comme les objets qui sont créés à l'aide de new
de syntaxe en C++ - ils sont créés sur le tas, mais à la différence des objets en C++, ils sont gérés par le moteur d'exécution, de sorte que vous n'avez pas à vous soucier de la destruction.
Étant donné que les objets sont toujours sur le tas le fait que les références de l'objet (c'est à dire des pointeurs) hors de portée devient inutile. Il y a plus de facteurs pour déterminer si un objet est perçu que de simplement la présence de références à l'objet.
C# références de l'Objet
Jon Skeet comparé les références de l'objet en Java à des morceaux de chaîne de caractères qui sont attachés au ballon, qui en est l'objet. Même analogie s'applique à C# références de l'objet. Ils suffit de pointer vers un emplacement du tas qui contient l'objet. Ainsi, la valeur null n'a pas d'effet immédiat sur la durée de vie des objets, le ballon continue d'exister jusqu'à ce que le GC "pop".
En continuant vers le bas du ballon analogie, il semble logique qu'une fois que le ballon n'a pas de cordes attachées à elle, il peut être détruit. En fait, c'est exactement la façon dont un décompte de références d'objets dans les langages non managés. Sauf que cette approche ne fonctionne pas pour les références circulaires très bien. Imaginez deux ballons qui sont attachés ensemble par une chaîne mais ni le ballon a une chaîne à autre chose. En vertu de la simple réf règles de comptage, ils continuent d'exister, même si le ballon entier groupe est "orpheline".
.NET les objets sont un peu comme des ballons d'hélium sous un toit. Lorsque le toit s'ouvre (GC fonctionne) - le solde non utilisé des ballons s'envolent, même s'il y a des groupes de ballons qui sont attachés ensemble.
.NET GC utilise une combinaison de intergénérationnel de la GC et de la marque et de balayage. Une approche générationnelle implique l'exécution favorisant d'inspecter les objets qui ont été attribuées, plus récemment, ils sont plus susceptibles d'être utilisées, et de marque et de balayage consiste à exécution en passant par l'ensemble de l'objet graphique et de travail s'il y a des groupes d'objets qui ne sont pas utilisés. Cette adéquatement traite de la circulaire problème de dépendance.
Aussi, .NET GC s'exécute sur un autre thread(soi-disant finaliseur thread) comme il a un peu de faire et de faire ce que sur le thread principal serait d'interrompre votre programme.
Comme d'autres l'ont dit, vous avez certainement envie d'appeler Dispose
si la classe implémente IDisposable
. Je prends un assez rigide position sur ce point. Certains pourraient prétendre qu'appelant Dispose
sur DataSet
, par exemple, est inutile, parce qu'ils démonté et vu qu'il n'a pas fait quelque chose de valable. Mais, je pense qu'il ya des idées fausses abondent dans cet argument.
Lire ceci pour un débat intéressant par le respect des individus sur le sujet. Alors lisez mon raisonnement ici pourquoi je pense que Jeffery Richter est dans le mauvais camp.
Maintenant, si oui ou non vous devez définir une référence à l' null
. La réponse est non. Permettez-moi d'illustrer mon propos avec le code suivant.
public static void Main()
{
Object a = new Object();
Console.WriteLine("object created");
DoSomething(a);
Console.WriteLine("object used");
a = null;
Console.WriteLine("reference set to null");
}
Alors, quand pensez-vous de l'objet référencé par a
est admissible pour la collecte? Si vous avez dit après l'appel à a = null
alors vous avez tort. Si vous avez dit après l' Main
méthode est terminée, alors vous êtes aussi mauvais. La réponse correcte est qu'il est admissible pour la collecte, parfois, lors de l'appel d' DoSomething
. Qui est à droite. Il est admissible avant la référence est définie à l' null
et peut-être même avant l'appel à l' DoSomething
complète. C'est parce que le compilateur JIT peut reconnaître lorsque les références de l'objet ne sont plus déréférencé, même si elles sont encore ancrés.
Si l'objet implémente IDisposable
, alors oui, vous devriez jeter. L'objet pourrait être accrochés à des ressources autochtones (les descripteurs de fichiers, OS objets) qui peut ne pas être immédiatement remis en liberté le contraire. Cela peut conduire à l'insuffisance de ressources, de verrouillage de fichiers et d'autres bogues subtils qui, autrement, pourraient être évités.
Voir également la mise en Œuvre d'une Méthode dispose sur MSDN.