Question

L' .NET IDisposable Modèle implique que si vous écrivez un finaliseur, et de mettre en œuvre IDisposable, que votre finaliseur besoin d'appeler explicitement la Jeter.Ce qui est logique, et c'est ce que j'ai toujours fait dans les rares cas où un finaliseur est justifiée.

Cependant, qu'advient-il si je viens de le faire:

class Foo : IDisposable
{
     public void Dispose(){ CloseSomeHandle(); }
}

et ne pas mettre en œuvre un outil de finalisation, ou quoi que ce soit.Va le cadre de l'appel de la méthode dispose pour moi?

Oui je sais que c'sons muets, et que la logique implique qu'il ne sera pas, mais j'ai toujours eu que 2 choses à l'arrière de ma tête qui m'ont fait de doute.

  1. Quelqu'un il y a quelques années m'a dit une fois qu'il serait en fait cela, et que cette personne avait une très solide track record "connaître leurs trucs."

  2. Le compilateur/cadre de l'autre la "magie" des choses en fonction de ce que les interfaces de vous mettre en œuvre (par exemple:foreach, les méthodes d'extension, la sérialisation basée sur les attributs, etc), il est donc logique que cela pourrait être la "magie" de trop.

Alors que j'ai lu beaucoup de choses sur elle, et il y a eu beaucoup de choses implicites, je n'ai jamais été en mesure de trouver un définitif Réponse oui ou Non à cette question.

Était-ce utile?

La solution

L' .Net Garbage Collector appelle l'Objet.Finaliser une méthode d'un objet sur la collecte des ordures.Par par défaut ce n' rien et doit être overidden si vous souhaitez libérer des ressources supplémentaires.

Jeter n'est PAS appelée automatiquement et doit être explicitement appelé si les ressources doivent être libérés, comme dans une "aide" ou "essayer, enfin," bloc

voir http://msdn.microsoft.com/en-us/library/system.object.finalize.aspx pour plus d'informations

Autres conseils

Je tiens à souligner Brian point dans son commentaire, parce que c'est important.

Les finaliseurs ne sont pas déterministes destructeurs comme en C++.Comme d'autres l'ont souligné, il n'existe aucune garantie que lorsqu'il sera appelé, et en effet, si vous avez assez de mémoire, si elle jamais être appelé.

Mais la mauvaise chose à propos de finaliseurs, c'est que, comme Brian l'a dit, il provoque votre objet de survivre à la collecte des ordures.Cela peut être mauvais.Pourquoi?

Comme vous pouvez ou ne pouvez pas savoir, la GC est divisé en générations - Gen 0, 1 et 2, ainsi que le Grand Tas d'Objets.Split est un lâche terme - vous obtenir un bloc de mémoire, mais il y a des pointeurs de l'endroit où les Gen 0 objets de début et de fin.

Le processus de la pensée est que vous aurez probablement utiliser beaucoup d'objets qui sera de courte durée.Donc, ceux qui devraient être faciles et rapides pour la GC pour obtenir à la génération 0 objets.Alors, quand il y a la pression de la mémoire, la première chose qu'il fait est un Gen 0 collection.

Maintenant, si cela ne résout pas assez de pression, puis il remonte et fait un Gen 1 balayage (rétablissement de la génération 0), et puis si toujours pas assez, il n'a Gen 2 de balayage (en refaisant la Gen 1 et de la génération 0).Donc, nettoyage à longue durée de vie des objets peut prendre du temps et être plutôt cher (puisque votre fils peut être suspendu pendant l'opération).

Cela signifie que si vous faites quelque chose comme cela:

~MyClass() { }

Votre objet, n'importe quoi, va vivre à la Génération 2.C'est parce que la GC n'a aucun moyen de l'appel de l'outil de finalisation au cours de la collecte des ordures.Afin de voir les objets qui doivent être finalisés sont déplacés vers une file d'attente spéciale pour être nettoyés par un autre thread (thread finaliseur - qui, si vous tuez fait toutes sortes de mauvaises choses se produisent).Cela signifie que vos objets de traîner plus longtemps, et potentiellement une force de plus garbage collections.

Donc, tout ça est juste pour les conduire à la maison le point que vous souhaitez utiliser IDisposable de nettoyer les ressources autant que possible et essayer sérieusement à trouver des façons de contourner à l'aide de l'outil de finalisation.C'est dans votre application à son meilleur intérêt.

Il ya beaucoup de bonnes discussion déjà ici, et je suis un peu en retard à la fête, mais je voulais ajouter quelques points à moi-même.

  • La Poubelle collecter ne sera jamais exécuter directement une méthode dispose pour vous.
  • La GC va exécuter les finaliseurs quand elle se sent comme elle.
  • Un modèle commun qui est utilisé pour les objets qui ont un finaliseur est celle de l'appel d'une méthode qui est, par convention, définie comme Dispose(bool disposing) en passant false pour indiquer que l'appel a été fait en raison de la finalisation plutôt qu'explicite Disposer d'appel.
  • C'est parce qu'il n'est pas sûr de faire des hypothèses sur d'autres objets gérés pendant la finalisation d'un objet (ils ont déjà été finalisés).

class SomeObject : IDisposable {
 IntPtr _SomeNativeHandle;
 FileStream _SomeFileStream;

 // Something useful here

 ~ SomeObject() {
  Dispose(false);
 }

 public void Dispose() {
  Dispose(true);
 }

 protected virtual void Dispose(bool disposing) {
  if(disposing) {
   GC.SuppressFinalize(this);
   //Because the object was explicitly disposed, there will be no need to 
   //run the finalizer.  Suppressing it reduces pressure on the GC

   //The managed reference to an IDisposable is disposed only if the 
   _SomeFileStream.Dispose();
  }

  //Regardless, clean up the native handle ourselves.  Because it is simple a member
  // of the current instance, the GC can't have done anything to it, 
  // and this is the onlyplace to safely clean up

  if(IntPtr.Zero != _SomeNativeHandle) {
   NativeMethods.CloseHandle(_SomeNativeHandle);
   _SomeNativeHandle = IntPtr.Zero;
  }
 }
}

C'est la version simple, mais il ya beaucoup de nuances qui peuvent vous passionner sur ce modèle.

  • Le contrat de IDisposable.Disposer indique qu'il doit être possible d'appeler plusieurs fois (l'appel de Disposer sur un objet qui a déjà été éliminés n'ont rien à faire)
  • Il peut devenir très compliqué à gérer correctement une hiérarchie d'héritage des objets jetables, surtout si différentes couches d'introduire de nouvelles Jetables et des ressources non managées.Dans le schéma ci-dessus dispose(bool) est virtuel pour lui permettre d'être remplacé, de sorte qu'il peut être géré, mais je trouve que c'est sujette à erreur.

À mon avis, il est beaucoup mieux d'éviter d'avoir tout les types qui contiennent directement les deux jetable, des références et des ressources autochtones qui peut exiger de finalisation.SafeHandles fournir très propre façon de faire cela par l'encapsulation des ressources autochtones dans les jetables que l'interne de fournir leurs propres finalisation (avec un certain nombre d'autres avantages, comme le retrait de la fenêtre au cours de P/Invoke où un natif de la poignée pourrait être perdu en raison d'un asynchrones exception).

Il suffit de définir un SafeHandle rend cette Triviale:


private class SomeSafeHandle
 : SafeHandleZeroOrMinusOneIsInvalid {
 public SomeSafeHandle()
  : base(true)
  { }

 protected override bool ReleaseHandle()
 { return NativeMethods.CloseHandle(handle); }
}

Permet de simplifier le type contenant à:


class SomeObject : IDisposable {
 SomeSafeHandle _SomeSafeHandle;
 FileStream _SomeFileStream;
 // Something useful here
 public virtual void Dispose() {
  _SomeSafeHandle.Dispose();
  _SomeFileStream.Dispose();
 }
}

Je ne le pense pas.Vous avez plus de contrôle lors de l'Éliminer est appelée, ce qui signifie que vous pourrait, en théorie, écrire disposition du code qui fait des hypothèses sur (par exemple) l'existence d'autres objets.Vous n'avez pas de contrôle sur le moment où le finaliseur est appelé, de sorte qu'il serait terrible d'avoir le finaliseur appeler automatiquement Disposer sur votre compte.


EDIT:Je suis parti et testé, juste pour s'assurer que:

class Program
{
    static void Main(string[] args)
    {
        Fred f = new Fred();
        f = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
        Console.WriteLine("Fred's gone, and he's not coming back...");
        Console.ReadLine();
    }
}

class Fred : IDisposable
{
    ~Fred()
    {
        Console.WriteLine("Being finalized");
    }

    void IDisposable.Dispose()
    {
        Console.WriteLine("Being Disposed");
    }
}

Pas dans le cas que vous décrivez, Mais le GC va appeler le Finaliseur pour vous, si vous en avez un.

CEPENDANT.La prochaine collecte de déchets ,au lieu d'être recueillies, l'objet va aller dans la finalisation québec, tout est connu, c'finaliseur de l'appelé.La prochaine collection après qu'il sera libéré.

En fonction de la pression sur la mémoire de votre application, vous ne pouvez pas avoir un gc pour que l'objet de la génération pour un certain temps.Ainsi, dans le cas de le dire, un flux de fichier ou d'une base, vous pourriez avoir à attendre un certain temps pour que la ressource non managée pour être libéré dans le finaliseur appel pour un tandis que, à l'origine de certains problèmes.

Non, il n'est pas appelé.

Mais ce qui rend facile pour n'oubliez pas de jeter vos objets.Utilisez simplement le using mot-clé.

J'ai fait le test suivant pour ceci:

class Program
{
    static void Main(string[] args)
    {
        Foo foo = new Foo();
        foo = null;
        Console.WriteLine("foo is null");
        GC.Collect();
        Console.WriteLine("GC Called");
        Console.ReadLine();
    }
}

class Foo : IDisposable
{
    public void Dispose()
    {

        Console.WriteLine("Disposed!");
    }

Le GC pas appel d'aliéner.Il peut appelez votre outil de finalisation, mais même cela n'est pas garanti dans toutes les circonstances.

Voir ce l'article pour une discussion de la meilleure façon de gérer cela.

La documentation sur IDisposable donne une assez clair et détaillé explication du comportement, ainsi que l'exemple de code.Le GC n'ira PAS en appel de la Dispose() méthode sur l'interface, mais il va appeler le finaliseur pour votre objet.

La IDisposable modèle a été créé principalement pour être appelé par le développeur, si vous avez un objet qui implémente IDispose le développeur doit soit mettre en œuvre la using mot-clé le contexte de l'objet ou de l'appel de la méthode dispose directement.

Le fail safe pour le motif est de mettre en œuvre l'outil de finalisation de l'appel de la méthode dispose ().Si vous ne le faites pas, vous risquez de créer quelques fuites de mémoire c'est à dire:Si vous créez des COM wrapper et de ne jamais appeler le Système.Moment de l'exécution.Interop.Marshall.ReleaseComObject(comObject) (ce qui serait placé dans la méthode dispose).

Il n'y a pas de magie dans le clr pour appeler des méthodes dispose automatiquement d'autres que le suivi des objets qui contiennent des finaliseurs et de les stocker dans le Finaliseur table par le GC et de les appeler lors de la partie de nettoyage heuristique coup de pied dans le GC.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top