66 votes

Pourquoi appeler dispose (false) dans le destructeur?

Ce qui suit est un exemple typique de motif de disposition:

  public bool IsDisposed { get; private set; }

  #region IDisposable Members

  public void Dispose()
  {
    Dispose(true);
    GC.SuppressFinalize(this);
  }

  protected virtual void Dispose(bool disposing)
  {
    if (!IsDisposed)
    {
      if (disposing)
      {
        //perform cleanup here
      }

      IsDisposed = true;
    }
  }

  ~MyObject()
  {
    Dispose(false);
  }
 

Je comprends ce que dispose, mais ce que je ne comprends pas, c'est pourquoi vous voudriez appeler dispose (faux) dans le destructeur? Si vous regardez la définition, cela ne ferait absolument rien, alors pourquoi quelqu'un voudrait-il écrire un code comme celui-ci? Ne serait-il pas logique de ne pas appeler du tout le destructeur?

51voto

Guffa Points 308133

Le finalizer est utilisé comme une solution de repli si l'objet n'est pas éliminés correctement pour une raison quelconque. Normalement l' Dispose() méthode serait appelé, ce qui supprime le finaliseur de branchement et transforme l'objet en un objet géré que le garbage collector peut facilement supprimer.

Voici un exemple à partir de MSDN d'une classe qui a réussi et les ressources non managées à nettoyer.

Notez que la gestion des ressources ne sont nettoyés si disposing est vrai, mais les ressources non managées est toujours nettoyés.

public class MyResource: IDisposable
{
    // Pointer to an external unmanaged resource.
    private IntPtr handle;
    // Other managed resource this class uses.
    private Component component = new Component();
    // Track whether Dispose has been called.
    private bool disposed = false;

    // The class constructor.
    public MyResource(IntPtr handle)
    {
        this.handle = handle;
    }

    // Implement IDisposable.
    // Do not make this method virtual.
    // A derived class should not be able to override this method.
    public void Dispose()
    {
        Dispose(true);
        // This object will be cleaned up by the Dispose method.
        // Therefore, you should call GC.SupressFinalize to
        // take this object off the finalization queue
        // and prevent finalization code for this object
        // from executing a second time.
        GC.SuppressFinalize(this);
    }

    // Dispose(bool disposing) executes in two distinct scenarios.
    // If disposing equals true, the method has been called directly
    // or indirectly by a user's code. Managed and unmanaged resources
    // can be disposed.
    // If disposing equals false, the method has been called by the
    // runtime from inside the finalizer and you should not reference
    // other objects. Only unmanaged resources can be disposed.
    private void Dispose(bool disposing)
    {
        // Check to see if Dispose has already been called.
        if(!this.disposed)
        {
            // If disposing equals true, dispose all managed
            // and unmanaged resources.
            if(disposing)
            {
                // Dispose managed resources.
                component.Dispose();
            }

            // Call the appropriate methods to clean up
            // unmanaged resources here.
            // If disposing is false,
            // only the following code is executed.
            CloseHandle(handle);
            handle = IntPtr.Zero;

            // Note disposing has been done.
            disposed = true;

        }
    }

    // Use interop to call the method necessary
    // to clean up the unmanaged resource.
    [System.Runtime.InteropServices.DllImport("Kernel32")]
    private extern static Boolean CloseHandle(IntPtr handle);

    // Use C# destructor syntax for finalization code.
    // This destructor will run only if the Dispose method
    // does not get called.
    // It gives your base class the opportunity to finalize.
    // Do not provide destructors in types derived from this class.
    ~MyResource()
    {
        // Do not re-create Dispose clean-up code here.
        // Calling Dispose(false) is optimal in terms of
        // readability and maintainability.
        Dispose(false);
    }
}

19voto

stuartd Points 22668

"L'idée ici est que Dispose(Boolean) sait si c'est appelé à faire explicite de nettoyage (le Booléen est vrai) et appelé en raison d'une collecte des ordures (le Booléen est faux). Cette la distinction est utile car, lorsque être disposé de façon explicite, l' Dispose(Boolean) méthode peut en toute sécurité exécuter du code en utilisant le type de référence les champs qui font référence à d'autres objets sachant bien sûr que ces autres les objets n'ont pas été finalisés ou éliminées encore. Lorsque le Booléen est faux, les Disposer(Boolean) méthode ne doit pas exécuter de code qui font référence à référence type de champs, parce que ces les objets peuvent avoir déjà été finalisé."

Il y a beaucoup plus d'infos ici..

9voto

John Saunders Points 118808

Il n'y a pas des destructeurs en C#. C'est un outil de finalisation, ce qui est une chose différente.

La distinction est de savoir si vous avez besoin pour nettoyer des objets gérés ou non. Vous ne voulez pas essayer de les nettoyer dans le finaliseur, comme ils l'ont peut-être été finalisé.


Je viens tout juste arrivé de regarder les Destructeurs de la page du Guide de Programmation C#. Il montre que je me suis trompé dans ma réponse ci-dessus. En particulier, il y a une différence entre destructeur et finalizer:

class Car
{
    ~Car()  // destructor
    {
        // cleanup statements...
    }
}

est équivalent à

protected override void Finalize()
{
    try
    {
        // Cleanup statements...
    }
    finally
    {
        base.Finalize();
    }
}

3voto

tvanfosson Points 268301

Je pense que la confusion est due au fait que, dans votre exemple, vous n'êtes pas libérer toutes les ressources non managées. Ces facteurs doivent également être libéré lors de l'dispose est appelé via la collecte des ordures et ils seraient libérés à l'extérieur de la vérification pour l' disposing. Consultez le site web MSDN exemple concernant la libérant des ressources non managées. Autre que cela pourrait/devrait se produire à l'extérieur de la case est un appel à toute la classe de base méthode dispose.

De la cité l'article:

   protected override void Dispose(bool disposing) 
   {
      if (disposing) 
      {
         // Release managed resources.
      }
      // Release unmanaged resources.
      // Set large fields to null.
      // Call Dispose on your base class.
      base.Dispose(disposing);
   }

1voto

ggf31416 Points 2060

À l'intérieur du if (disposing), vous êtes censé appeler dispos / close sur des objets gérés disposant de ressources non gérées (par exemple, des connexions à une base de données). Lorsque le finaliseur s'appelle, ces objets ne sont plus accessibles, de sorte que les objets eux-mêmes peuvent être finalisés. besoin d'appeler disposer sur eux. De plus, l'ordre de finalisation est indéterminé, vous pouvez donc appeler la suppression sur des objets déjà supprimés.

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