Implémentation d'une méthode Dispose

La méthode Dispose d'un type doit libérer toutes les ressources qu'elle possède. Elle doit également libérer toutes les ressources détenues par ses types de base en appelant la méthode Dispose de son type parent. La méthode Dispose du type parent doit libérer toutes les ressources qu'il possède et appeler à son tour la méthode Dispose de son type parent, propageant ainsi ce modèle dans la hiérarchie des types de base. Pour que les ressources soient toujours assurées d'être correctement nettoyées, une méthode Dispose doit pouvoir être appelée à plusieurs reprises sans lever d'exception.

Important

Les programmeurs C++ ne doivent pas utiliser cette rubrique. Consultez plutôt Destructors and Finalizers in Visual C++. Dans le .NET Framework version 2.0, le compilateur C++ fournit la prise en charge l'implémentation de la suppression déterminable de ressources et n'autorise pas d'implémentation directe de la méthode Dispose.

Une méthode Dispose doit appeler la méthode GC.SuppressFinalize de l'objet qu'elle supprime. Si l'objet figure actuellement dans la file d'attente de finalisation, GC.SuppressFinalize empêche sa méthode Finalize d'être appelée. N'oubliez pas que l'exécution d'une méthode Finalize est une opération coûteuse. Si votre méthode Dispose a déjà procédé au nettoyage de l'objet, il est dans ce cas inutile que le garbage collector appelle la méthode Finalize de l'objet.

Notes

L'exemple de code indiqué pour la méthode System.GC.KeepAlive(System.Object) affiche la façon dont un recyclage agressif peut entraîner l'exécution d'un finaliseur pendant qu'un membre de l'objet demandé se trouve en cours d'exécution. Il est conseillé d'appeler la méthode KeepAlive à la fin d'une méthode Dispose longue.

Le but de l'exemple de code suivant est d'illustrer le modèle de design recommandé concernant l'implémentation d'une méthode Dispose pour des classes qui encapsulent des ressources non managées. Ce modèle est implémenté partout dans le .NET Framework.

Les classes de ressource sont généralement dérivées de classes natives complexes ou d'API et doivent être personnalisées en conséquence. Utilisez ce modèle de code comme point de départ pour la création d'une classe de ressource et effectuez la personnalisation nécessaire en fonction des ressources que vous encapsulez. Vous ne pouvez pas compiler cet exemple et l'utiliser directement dans une application.

Dans cet exemple, la classe de base BaseResource implémente une méthode Dispose publique qui peut être appelée par les utilisateurs de la classe. Elle appelle à son tour la méthode virtual Dispose(bool disposing) (virtual Dispose(disposing As Boolean) en Visual Basic). Les valeurs passées sont soit true, soit false selon l'identité de l'appelant. Le code de nettoyage approprié pour l'objet est exécuté dans la méthode Dispose virtuelle.

La méthode Dispose(bool disposing) exécute deux scénarios distincts. Si disposing a la valeur true, la méthode a été appelée directement ou indirectement par un code d'utilisateur et les ressources managées et non managées peuvent être supprimées. Si disposing a la valeur false, la méthode a été appelée par le runtime à partir du finaliseur et seules les ressources non managées peuvent être supprimées. Lorsqu'un objet exécute son code de finalisation, il ne doit pas référencer d'autres objets, parce que les finaliseurs ne s'exécutent pas dans un ordre particulier. Si un finaliseur en cours d'exécution référence un autre objet qui a déjà été finalisé, le finaliseur en cours d'exécution échoue.

La classe de base fournit un destructeur ou une méthode Finalize jouant un rôle de protection au cas où la méthode Dispose ne serait pas appelée. La méthode Finalize appelle la méthode Dispose qui prend des paramètres, en passant la valeur false. Vous ne devez pas recréer le code de nettoyage de la méthode Dispose dans la méthode Finalize. L'appel de Dispose(false) est optimal pour la lisibilité et la gestion du code.

La classe MyResourceWrapper illustre la dérivation d'une classe qui implémente la gestion des ressources à l'aide de la méthode Dispose. MyResourceWrapper substitue virtual Dispose(bool disposing) et fournit un code de nettoyage pour les ressources managées et non managées qu'elle crée. MyResourceWrapper appelle également Dispose sur sa classe de base BaseResource pour s'assurer que sa base peut être nettoyée correctement. Notez que la classe dérivée MyResourceWrapper ne possède pas de méthode Finalize ou de méthode Dispose sans paramètres, parce qu'elle hérite de ceux de la classe de base BaseResource.

Notes

Dans cet exemple, la méthode protected Dispose(bool disposing) n'applique pas la sécurité des threads, car la méthode ne peut pas être simultanément appelée à partir d'un thread utilisateur et d'un thread finaliseur. En outre, une application cliente utilisant BaseResource ne doit jamais autoriser plusieurs threads utilisateur à appeler la méthode protected Dispose(bool disposing) simultanément. Une application ou une bibliothèque de classes doivt être conçue pour n'autoriser qu'un thread à posséder la durée de vie d'une ressource et à appeler Dispose lorsque la ressource n'est plus nécessaire. Selon la ressource, l'accès non synchronisé à un thread lors de la suppression de ressources peut poser des problèmes de sécurité. Les développeurs doivent passer en revue leur code avec attention afin de déterminer la meilleure approche concernant l'application de la sécurité des threads.

' Design pattern for the base class.
' By implementing IDisposable, you are announcing that instances
' of this type allocate scarce resources.
Public Class BaseResource
   Implements IDisposable
   ' Pointer to an external unmanaged resource.
   Private handle As IntPtr 
   ' Other managed resource this class uses.
   Private Components As Component
   ' Track whether Dispose has been called.
   Private disposed As Boolean = False

   ' Constructor for the BaseResource Object.
   Public Sub New()
      ' Insert appropriate constructor code here.
   End Sub

   ' Implement IDisposable.
   ' Do not make this method Overridable.
   ' A derived class should not be able to override this method.
   Public Overloads Sub Dispose()Implements IDisposable.Dispose
      Dispose(true)
      ' Take yourself off of the finalization queue
      ' to prevent finalization code for this object
      ' from executing a second time.
      GC.SuppressFinalize(Me) 
   End Sub

' Dispose(disposing As Boolean) executes in two distinct scenarios.
' If disposing is 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.
Protected Overloads Overridable Sub Dispose(disposing As Boolean)
   ' Check to see if Dispose has already been called.
   If Not (Me.disposed) Then
      ' If disposing equals true, dispose all managed 
      ' and unmanaged resources.
      If (disposing) Then
         ' Dispose managed resources.
         Components.Dispose()
      End If
      ' Release unmanaged resources. If disposing is false,
      ' only the following code is executed.      
      CloseHandle(handle)
      handle = IntPtr.Zero
      ' Note that this is not thread safe.
      ' Another thread could start disposing the object
      ' after the managed resources are disposed,
      ' but before the disposed flag is set to true.
      ' If thread safety is necessary, it must be
      ' implemented by the client.
   End If
   Me.disposed = true
End Sub

   ' This Finalize method will run only if the 
   ' Dispose method does not get called.
   ' By default, methods are NotOverridable. 
   ' This prevents a derived class from overriding this method.
   Protected Overrides Sub Finalize()
         ' Do not re-create Dispose clean-up code here.
         ' Calling Dispose(false) is optimal in terms of
         ' readability and maintainability.
         Dispose(false)
   End Sub
   
   ' Allow your Dispose method to be called multiple times,
   ' but throw an exception if the object has been disposed.
   ' Whenever you do something with this class, 
   ' check to see if it has been disposed.
   Public Sub DoSomething()
      If Me.disposed Then
         Throw New ObjectDisposedException()
      End if
   End Sub
End Class

' Design pattern for a derived class.
' Note that this derived class inherently implements the 
' IDisposable interface because it is implemented in the base class.
Public Class MyResourceWrapper
   Inherits BaseResource
   
   ' A managed resource that you add in this derived class.
   private addedManaged As ManagedResource
   ' A native unmanaged resource that you add in this derived class.
   private addedNative As NativeResource
   ' Track whether Dispose has been called.
   Private disposed As Boolean = False

   ' Constructor for the MyResourceWrapper object.
   Public Sub New()      
      MyBase.New()
      ' Insert appropriate constructor code here for the
      ' added resources.
   End Sub

   Protected Overloads Overrides Sub Dispose(disposing As Boolean)
      If Not (Me.disposed) Then
         Try
            If disposing Then
              ' Release the managed resources you added in
              ' this derived class here.
              addedManaged.Dispose()
            End If
            ' Release the native unmanaged resources you added
            ' in this derived class here.
            CloseHandle(addedNative)
            Me.disposed = true
         Finally
            ' Call Dispose on your base class.
            MyBase.Dispose(disposing)
         End Try
      End If
   End Sub
End Class
' This derived class does not have a Finalize method
' or a Dispose method without parameters because it 
' inherits them from the base class.
// Design pattern for the base class.
// By implementing IDisposable, you are announcing that instances
// of this type allocate scarce resources.
public class BaseResource: IDisposable
{
   // Pointer to an external unmanaged resource.
   private IntPtr handle;
   // Other managed resource this class uses.
   private Component Components;
   // Track whether Dispose has been called.
   private bool disposed = false;

   // Constructor for the BaseResource object.
   public BaseResource()
   {
      // Insert appropriate constructor code here.
   }

   // Implement IDisposable.
   // Do not make this method virtual.
   // A derived class should not be able to override this method.
   public void Dispose()
   {
      Dispose(true);
      // Take yourself off the Finalization queue 
      // to 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.
   protected virtual 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.
            Components.Dispose();
         }
         // Release unmanaged resources. If disposing is false, 
         // only the following code is executed.
         CloseHandle(handle);
         handle = IntPtr.Zero;
         // Note that this is not thread safe.
         // Another thread could start disposing the object
         // after the managed resources are disposed,
         // but before the disposed flag is set to true.
         // If thread safety is necessary, it must be
         // implemented by the client.

      }
      disposed = true;         
   }

   // 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.
   ~BaseResource()      
   {
      // Do not re-create Dispose clean-up code here.
      // Calling Dispose(false) is optimal in terms of
      // readability and maintainability.
      Dispose(false);
   }

   // Allow your Dispose method to be called multiple times,
   // but throw an exception if the object has been disposed.
   // Whenever you do something with this class, 
   // check to see if it has been disposed.
   public void DoSomething()
   {
      if(this.disposed)
      {
         throw new ObjectDisposedException();
      }
   }
}

// Design pattern for a derived class.
// Note that this derived class inherently implements the 
// IDisposable interface because it is implemented in the base class.
public class MyResourceWrapper: BaseResource
{
   // A managed resource that you add in this derived class.
   private ManagedResource addedManaged;
   // A native unmanaged resource that you add in this derived class.
   private NativeResource addedNative;
   private bool disposed = false;

  // Constructor for this object.
   public MyResourceWrapper()
   {
      // Insert appropriate constructor code here.
   }

   protected override void Dispose(bool disposing)
   {
      if(!this.disposed)
      {
         try
         {
            if(disposing)
            {
               // Release the managed resources you added in
               // this derived class here.
               addedManaged.Dispose();         
            }
            // Release the native unmanaged resources you added
            // in this derived class here.
            CloseHandle(addedNative);
            this.disposed = true;
         }
         finally
         {
            // Call Dispose on your base class.
            base.Dispose(disposing);
         }
      }
   }
}

// This derived class does not have a Finalize method
// or a Dispose method without parameters because it inherits 
// them from the base class.

Implémentation d'une méthode Close

Pour les types pour lesquels l'appel d'une méthode Close est plus naturel que l'appel d'une méthode Dispose, ajoutez une méthode Close publique au type de base. La méthode Close appelle à son tour la méthode Dispose sans paramètre, qui effectue les opérations de nettoyage appropriées. L'exemple de code suivant illustre une méthode Close.

' Do not make this method Overridable.
' A derived class should not be allowed
' to override this method.
Public Sub Close()
   ' Calls the Dispose method without parameters.
   Dispose()
End Sub
// Do not make this method virtual.
// A derived class should not be allowed
// to override this method.
public void Close()
{
   // Calls the Dispose method without parameters.
   Dispose();
}

Voir aussi

Référence

GC.SuppressFinalize Method
Destructors and Finalizers in Visual C++
Implémentation des méthodes Finalize et Dispose pour nettoyer des ressources non managées

Concepts

Substitution de la méthode Finalize