Implémentation d’une méthode DisposeImplementing a Dispose method

Vous implémentez une méthode Dispose pour libérer les ressources non managées utilisées par votre application.You implement a Dispose method to release unmanaged resources used by your application. Le Garbage collector .NET n’alloue pas de mémoire non managée, et n’en libère pas non plus.The .NET garbage collector does not allocate or release unmanaged memory.

Le modèle pour supprimer un objet, dénommé modèle de suppression, impose un ordre sur la durée de vie d’un objet.The pattern for disposing an object, referred to as a dispose pattern, imposes order on the lifetime of an object. Le modèle de suppression est utilisé uniquement pour les objets qui accèdent à des ressources non managées, telles que les handles de fichiers et de canaux, les handles d’attente, les handles d’attente ou les pointeurs vers les blocs de mémoire non managée.The dispose pattern is used only for objects that access unmanaged resources, such as file and pipe handles, registry handles, wait handles, or pointers to blocks of unmanaged memory. Cela est dû au fait que le récupérateur de mémoire est très efficace pour récupérer les objets managés inutilisés, mais ne peut pas récupérer les objets non managés.This is because the garbage collector is very efficient at reclaiming unused managed objects, but it is unable to reclaim unmanaged objects.

Le modèle de suppression comporte deux variantes :The dispose pattern has two variations:

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.To help ensure that resources are always cleaned up appropriately, a Dispose method should be callable multiple times without throwing an exception.

L'exemple de code indiqué pour la méthode GC.KeepAlive affiche la façon dont un garbage collection 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.The code example provided for the GC.KeepAlive method shows how aggressive garbage collection can cause a finalizer to run while a member of the reclaimed object is still executing. Il est conseillé d'appeler la méthode KeepAlive à la fin d'une méthode Dispose longue.It is a good idea to call the KeepAlive method at the end of a lengthy Dispose method.

Dispose() et Dispose(Boolean)Dispose() and Dispose(Boolean)

L'interface IDisposable requiert l'implémentation d'une méthode unique sans paramètre, Dispose.The IDisposable interface requires the implementation of a single parameterless method, Dispose. Toutefois, le modèle de suppression requiert deux méthodes Dispose à implémenter :However, the dispose pattern requires two Dispose methods to be implemented:

  • Implémentation non virtuelle publique de (NonInheritable en Visual Basic) IDisposable.Dispose qui n'a aucun paramètre.A public non-virtual (NonInheritable in Visual Basic) IDisposable.Dispose implementation that has no parameters.

  • Méthode virtuelle (Overridable en Visual Basic) Dispose dont la signature est :A protected virtual (Overridable in Visual Basic) Dispose method whose signature is:

    protected virtual void Dispose(bool disposing)
    
    Protected Overridable Sub Dispose(disposing As Boolean)
    

Surcharge de Dispose()The Dispose() overload

Comme la méthode NonInheritable sans paramètre non virtuelle (Dispose en Visual Basic) publique est appelée par un consommateur de type, son objectif est de libérer les ressources non managées et d'indiquer que le finaliseur, s'il en existe un, ne doit pas s'exécuter.Because the public, non-virtual (NonInheritable in Visual Basic), parameterless Dispose method is called by a consumer of the type, its purpose is to free unmanaged resources and to indicate that the finalizer, if one is present, doesn't have to run. De ce fait, son implémentation standard est la suivante :Because of this, it has a standard implementation:

public void Dispose()
{
   // Dispose of unmanaged resources.
   Dispose(true);
   // Suppress finalization.
   GC.SuppressFinalize(this);
}
Public Sub Dispose() _
           Implements IDisposable.Dispose
   ' Dispose of unmanaged resources.
   Dispose(True)
   ' Suppress finalization.
   GC.SuppressFinalize(Me)
End Sub

La méthode Dispose effectue le nettoyage de tous les objets, le récupérateur de mémoire n'a plus donc besoin d'appeler la remplacement de Object.Finalize des objets.The Dispose method performs all object cleanup, so the garbage collector no longer needs to call the objects' Object.Finalize override. Par conséquent, l'appel à la méthode SuppressFinalize empêche le récupérateur de mémoire d'exécuter le finaliseur.Therefore, the call to the SuppressFinalize method prevents the garbage collector from running the finalizer. Si le type n'a pas de finaliseur, l'appel à GC.SuppressFinalize n'a aucun effet.If the type has no finalizer, the call to GC.SuppressFinalize has no effect. Notez que le travail réel de libération des ressources non managées est effectué par la deuxième surcharge de la méthode Dispose.Note that the actual work of releasing unmanaged resources is performed by the second overload of the Dispose method.

Surcharge de Dispose(Boolean)The Dispose(Boolean) overload

Dans la seconde surcharge, le paramètre disposing est un Boolean qui indique si l'appel de la méthode provient d'une méthode Dispose (sa valeur est true) ou d'un finaliseur (sa valeur est false).In the second overload, the disposing parameter is a Boolean that indicates whether the method call comes from a Dispose method (its value is true) or from a finalizer (its value is false).

Le corps de la méthode se compose de deux blocs de code :The body of the method consists of two blocks of code:

  • Un bloc qui libère les ressources non managées.A block that frees unmanaged resources. Ce bloc s'exécute indépendamment de la valeur du paramètre disposing.This block executes regardless of the value of the disposing parameter.

  • Un bloc conditionnel qui libère les ressources managées.A conditional block that frees managed resources. Ce bloc s'exécute si la valeur de disposing est true.This block executes if the value of disposing is true. Les ressources managées qu'il libère peuvent inclure :The managed resources that it frees can include:

    Objets managés qui implémentent IDisposable.Managed objects that implement IDisposable. Le bloc conditionnel peut être utilisé pour appeler leur implémentation de Dispose.The conditional block can be used to call their Dispose implementation. Si vous avez utilisé un handle sécurisé pour encapsuler votre ressource non managée, vous devez appeler l'implémentation de SafeHandle.Dispose(Boolean) ici.If you have used a safe handle to wrap your unmanaged resource, you should call the SafeHandle.Dispose(Boolean) implementation here.

    Objets managés qui consomment de grandes quantités de mémoire ou consomment des ressources rares.Managed objects that consume large amounts of memory or consume scarce resources. La libération de ces objets explicitement dans la méthode Dispose les libère plus rapidement que s'ils ont été récupérés de façon non déterministe par le récupérateur de mémoire.Freeing these objects explicitly in the Dispose method releases them faster than if they were reclaimed non-deterministically by the garbage collector.

Si l’appel de la méthode vient d’un finaliseur (autrement dit, si disposing a la valeur false), seul le code qui libère les ressources non managées s’exécute.If the method call comes from a finalizer (that is, if disposing is false), only the code that frees unmanaged resources executes. Étant donné que l'ordre dans lequel le récupérateur de mémoire détruit les objets managés pendant la finalisation n'est pas défini, l'appel de cette surcharge Dispose avec la valeur false empêche le finaliseur d'essayer de libérer les ressources managées qui peuvent avoir déjà été récupérées.Because the order in which the garbage collector destroys managed objects during finalization is not defined, calling this Dispose overload with a value of false prevents the finalizer from trying to release managed resources that may have already been reclaimed.

Implémentation du modèle de suppression d'une classe de baseImplementing the dispose pattern for a base class

Si vous implémentez le modèle de suppression d’une classe de base, vous devez spécifier ce qui suit :If you implement the dispose pattern for a base class, you must provide the following:

Important

Vous devez implémenter ce modèle pour toutes les classes de base qui implémentent Dispose() et qui ne sont pas sealed (NotInheritable en Visual Basic).You should implement this pattern for all base classes that implement Dispose() and are not sealed (NotInheritable in Visual Basic).

  • Une implémentation de Dispose qui appelle la méthode Dispose(Boolean).A Dispose implementation that calls the Dispose(Boolean) method.

  • Une méthode Dispose(Boolean) qui effectue le travail réel de libération des ressources.A Dispose(Boolean) method that performs the actual work of releasing resources.

  • Une classe dérivée de SafeHandle qui encapsule votre ressource managée (recommandée) ou une substitution de la méthode Object.Finalize.Either a class derived from SafeHandle that wraps your unmanaged resource (recommended), or an override to the Object.Finalize method. La classe SafeHandle fournit un finaliseur qui vous permet de ne pas avoir à en coder un.The SafeHandle class provides a finalizer that frees you from having to code one.

Voici le modèle général d’implémentation du modèle de suppression d’une classe de base qui utilise un handle sécurisé.Here's the general pattern for implementing the dispose pattern for a base class that uses a safe handle.

using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;

class BaseClass : IDisposable
{
   // Flag: Has Dispose already been called?
   bool disposed = false;
   // Instantiate a SafeHandle instance.
   SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true);
   
   // Public implementation of Dispose pattern callable by consumers.
   public void Dispose()
   { 
      Dispose(true);
      GC.SuppressFinalize(this);           
   }
   
   // Protected implementation of Dispose pattern.
   protected virtual void Dispose(bool disposing)
   {
      if (disposed)
         return; 
      
      if (disposing) {
         handle.Dispose();
         // Free any other managed objects here.
         //
      }
      
      disposed = true;
   }
}
Imports Microsoft.Win32.SafeHandles
Imports System.Runtime.InteropServices

Class BaseClass : Implements IDisposable
   ' Flag: Has Dispose already been called?
   Dim disposed As Boolean = False
   ' Instantiate a SafeHandle instance.
   Dim handle As SafeHandle = New SafeFileHandle(IntPtr.Zero, True)

   ' Public implementation of Dispose pattern callable by consumers.
   Public Sub Dispose() _
              Implements IDisposable.Dispose
      Dispose(True)
      GC.SuppressFinalize(Me)           
   End Sub
   
   ' Protected implementation of Dispose pattern.
   Protected Overridable Sub Dispose(disposing As Boolean)
      If disposed Then Return
      
      If disposing Then
         handle.Dispose()
         ' Free any other managed objects here.
         '
      End If
      
      disposed = True
   End Sub
End Class

Notes

L'exemple précédent utilise un objet SafeFileHandle pour illustrer le modèle, mais il est possible d'utiliser à la place n'importe quel objet dérivé de SafeHandle.The previous example uses a SafeFileHandle object to illustrate the pattern; any object derived from SafeHandle could be used instead. Notez que l'exemple n'instancie pas correctement son objet SafeFileHandle.Note that the example does not properly instantiate its SafeFileHandle object.

Voici le modèle général d’implémentation du modèle de suppression d’une classe de base qui remplace Object.Finalize.Here's the general pattern for implementing the dispose pattern for a base class that overrides Object.Finalize.

using System;

class BaseClass : IDisposable
{
   // Flag: Has Dispose already been called?
   bool disposed = false;
   
   // Public implementation of Dispose pattern callable by consumers.
   public void Dispose()
   { 
      Dispose(true);
      GC.SuppressFinalize(this);           
   }
   
   // Protected implementation of Dispose pattern.
   protected virtual void Dispose(bool disposing)
   {
      if (disposed)
         return; 
      
      if (disposing) {
         // Free any other managed objects here.
         //
      }
      
      // Free any unmanaged objects here.
      //
      disposed = true;
   }

   ~BaseClass()
   {
      Dispose(false);
   }
}
Class BaseClass : Implements IDisposable
   ' Flag: Has Dispose already been called?
   Dim disposed As Boolean = False
   
   ' Public implementation of Dispose pattern callable by consumers.
   Public Sub Dispose() _
              Implements IDisposable.Dispose
      Dispose(True)
      GC.SuppressFinalize(Me)           
   End Sub
   
   ' Protected implementation of Dispose pattern.
   Protected Overridable Sub Dispose(disposing As Boolean)
      If disposed Then Return
      
      If disposing Then
         ' Free any other managed objects here.
         '
      End If
      
      ' Free any unmanaged objects here.
      '
      disposed = True
   End Sub

   Protected Overrides Sub Finalize()
      Dispose(False)      
   End Sub
End Class

Notes

En C#, vous substituez Object.Finalize en définissant un destructeur.In C#, you override Object.Finalize by defining a destructor.

Implémentation du modèle de suppression d’une classe dérivéeImplementing the dispose pattern for a derived class

Une classe dérivée d'une classe qui implémente l'interface IDisposable ne doit pas implémenter IDisposable, car l'implémentation de la classe de base de IDisposable.Dispose est héritée par les classes dérivées.A class derived from a class that implements the IDisposable interface shouldn't implement IDisposable, because the base class implementation of IDisposable.Dispose is inherited by its derived classes. À la place, pour implémenter le modèle de suppression d’une classe dérivée, vous fournissez ce qui suit :Instead, to implement the dispose pattern for a derived class, you provide the following:

  • Une méthode protected Dispose(Boolean) qui substitue la méthode de la classe de base et effectue le travail réel de libération des ressources de la classe dérivée.A protected Dispose(Boolean) method that overrides the base class method and performs the actual work of releasing the resources of the derived class. Cette méthode doit également appeler la méthode Dispose(Boolean) de la classe de base et lui passer son état disposing pour l’argument.This method should also call the Dispose(Boolean) method of the base class and pass its disposing status for the argument.

  • Une classe dérivée de SafeHandle qui encapsule votre ressource managée (recommandée) ou une substitution de la méthode Object.Finalize.Either a class derived from SafeHandle that wraps your unmanaged resource (recommended), or an override to the Object.Finalize method. La classe SafeHandle fournit un finaliseur qui vous permet de ne pas avoir à en coder un.The SafeHandle class provides a finalizer that frees you from having to code one. Si vous fournissez un finaliseur, il doit appeler la surcharge Dispose(Boolean) avec un argument disposing égal à false.If you do provide a finalizer, it should call the Dispose(Boolean) overload with a disposing argument of false.

Voici le modèle général d’implémentation du modèle de suppression d’une classe dérivée qui utilise un handle sécurisé :Here's the general pattern for implementing the dispose pattern for a derived class that uses a safe handle:

using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;

class DerivedClass : BaseClass
{
   // Flag: Has Dispose already been called?
   bool disposed = false;
   // Instantiate a SafeHandle instance.
   SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true);

   // Protected implementation of Dispose pattern.
   protected override void Dispose(bool disposing)
   {
      if (disposed)
         return; 
      
      if (disposing) {
         handle.Dispose();
         // Free any other managed objects here.
         //
      }
      
      // Free any unmanaged objects here.
      //

      disposed = true;
      // Call base class implementation.
      base.Dispose(disposing);
   }
}
Imports Microsoft.Win32.SafeHandles
Imports System.Runtime.InteropServices

Class DerivedClass : Inherits BaseClass 
   ' Flag: Has Dispose already been called?
   Dim disposed As Boolean = False
   ' Instantiate a SafeHandle instance.
   Dim handle As SafeHandle = New SafeFileHandle(IntPtr.Zero, True)

   ' Protected implementation of Dispose pattern.
   Protected Overrides Sub Dispose(disposing As Boolean)
      If disposed Then Return
      
      If disposing Then
         handle.Dispose()
         ' Free any other managed objects here.
         '
      End If
      
      ' Free any unmanaged objects here.
      '
      disposed = True
      
      ' Call base class implementation.
      MyBase.Dispose(disposing)
   End Sub
End Class

Notes

L'exemple précédent utilise un objet SafeFileHandle pour illustrer le modèle, mais il est possible d'utiliser à la place n'importe quel objet dérivé de SafeHandle.The previous example uses a SafeFileHandle object to illustrate the pattern; any object derived from SafeHandle could be used instead. Notez que l'exemple n'instancie pas correctement son objet SafeFileHandle.Note that the example does not properly instantiate its SafeFileHandle object.

Voici le modèle général d'implémentation du modèle de suppression d'une classe dérivée qui remplace Object.Finalize :Here's the general pattern for implementing the dispose pattern for a derived class that overrides Object.Finalize:

using System;

class DerivedClass : BaseClass
{
   // Flag: Has Dispose already been called?
   bool disposed = false;
   
   // Protected implementation of Dispose pattern.
   protected override void Dispose(bool disposing)
   {
      if (disposed)
         return; 
      
      if (disposing) {
         // Free any other managed objects here.
         //
      }
      
      // Free any unmanaged objects here.
      //
      disposed = true;
      
      // Call the base class implementation.
      base.Dispose(disposing);
   }

   ~DerivedClass()
   {
      Dispose(false);
   }
}
Class DerivedClass : Inherits BaseClass
   ' Flag: Has Dispose already been called?
   Dim disposed As Boolean = False
   
   ' Protected implementation of Dispose pattern.
   Protected Overrides Sub Dispose(disposing As Boolean)
      If disposed Then Return
      
      If disposing Then
         ' Free any other managed objects here.
         '
      End If
      
      ' Free any unmanaged objects here.
      '
      disposed = True

      ' Call the base class implementation.
      MyBase.Dispose(disposing)
   End Sub
   
   Protected Overrides Sub Finalize()
      Dispose(False)
   End Sub 
End Class

Notes

En C#, vous substituez Object.Finalize en définissant un destructeur.In C#, you override Object.Finalize by defining a destructor.

Utilisation des handles sécurisésUsing safe handles

L’écriture de code pour le finaliseur d’un objet est une tâche complexe qui peut provoquer des problèmes si elle n’est pas effectuée correctement.Writing code for an object's finalizer is a complex task that can cause problems if not done correctly. Par conséquent, nous vous recommandons de construire des objets System.Runtime.InteropServices.SafeHandle au lieu d'implémenter un finaliseur.Therefore, we recommend that you construct System.Runtime.InteropServices.SafeHandle objects instead of implementing a finalizer.

Les classes dérivées de la classe System.Runtime.InteropServices.SafeHandle simplifient les problèmes de durée de vie des objets en assignant et en libérant des handles sans interruption.Classes derived from the System.Runtime.InteropServices.SafeHandle class simplify object lifetime issues by assigning and releasing handles without interruption. Elles contiennent un finaliseur critique dont le fonctionnement pendant le déchargement d'un domaine d'application est garanti.They contain a critical finalizer that is guaranteed to run while an application domain is unloading. Pour plus d'informations sur les avantages de l'utilisation d'un handle sécurisé, consultez System.Runtime.InteropServices.SafeHandle.For more information about the advantages of using a safe handle, see System.Runtime.InteropServices.SafeHandle. Les classes dérivées suivantes de l'espace de noms Microsoft.Win32.SafeHandles fournissent des handles sécurisés :The following derived classes in the Microsoft.Win32.SafeHandles namespace provide safe handles:

Utilisation d’un handle sécurisé pour implémenter le modèle de suppression d’une classe de baseUsing a safe handle to implement the dispose pattern for a base class

L'exemple suivant illustre le modèle de suppression d'une classe de base, DisposableStreamResource, qui utilise un handle sécurisé pour encapsuler les ressources non managées.The following example illustrates the dispose pattern for a base class, DisposableStreamResource, that uses a safe handle to encapsulate unmanaged resources. Il définit une classe DisposableResource qui utilise SafeFileHandle pour encapsuler un objet Stream qui représente un fichier ouvert.It defines a DisposableResource class that uses a SafeFileHandle to wrap a Stream object that represents an open file. La méthode DisposableResource inclut également une seule propriété, Size, qui retourne le nombre total d'octets du flux de fichier.The DisposableResource method also includes a single property, Size, that returns the total number of bytes in the file stream.

using Microsoft.Win32.SafeHandles;
using System;
using System.IO;
using System.Runtime.InteropServices;

public class DisposableStreamResource : IDisposable
{
   // Define constants.
   protected const uint GENERIC_READ = 0x80000000;
   protected const uint FILE_SHARE_READ = 0x00000001;
   protected const uint OPEN_EXISTING = 3;
   protected const uint FILE_ATTRIBUTE_NORMAL = 0x80;
   protected IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
   private const int INVALID_FILE_SIZE = unchecked((int) 0xFFFFFFFF);
   
   // Define Windows APIs.
   [DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode)]
   protected static extern IntPtr CreateFile (
                                  string lpFileName, uint dwDesiredAccess, 
                                  uint dwShareMode, IntPtr lpSecurityAttributes, 
                                  uint dwCreationDisposition, uint dwFlagsAndAttributes, 
                                  IntPtr hTemplateFile);
   
   [DllImport("kernel32.dll")]
   private static extern int GetFileSize(SafeFileHandle hFile, out int lpFileSizeHigh);
    
   // Define locals.
   private bool disposed = false;
   private SafeFileHandle safeHandle; 
   private long bufferSize;
   private int upperWord;
   
   public DisposableStreamResource(string filename)
   {
      if (filename == null)
         throw new ArgumentNullException("The filename cannot be null.");
      else if (filename == "")
         throw new ArgumentException("The filename cannot be an empty string.");
            
      IntPtr handle = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ,
                                 IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
                                 IntPtr.Zero);
      if (handle != INVALID_HANDLE_VALUE)
         safeHandle = new SafeFileHandle(handle, true);
      else
         throw new FileNotFoundException(String.Format("Cannot open '{0}'", filename));
      
      // Get file size.
      bufferSize = GetFileSize(safeHandle, out upperWord); 
      if (bufferSize == INVALID_FILE_SIZE)
         bufferSize = -1;
      else if (upperWord > 0) 
         bufferSize = (((long)upperWord) << 32) + bufferSize;
   }
   
   public long Size 
   { get { return bufferSize; } }

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

   protected virtual void Dispose(bool disposing)
   {
      if (disposed) return;

      // Dispose of managed resources here.
      if (disposing)
         safeHandle.Dispose();
      
      // Dispose of any unmanaged resources not wrapped in safe handles.
      
      disposed = true;
   }  
}
Imports Microsoft.Win32.SafeHandles
Imports System.IO

Public Class DisposableStreamResource : Implements IDisposable
   ' Define constants.
   Protected Const GENERIC_READ As UInteger = &H80000000ui
   Protected Const FILE_SHARE_READ As UInteger = &H0000000i
   Protected Const OPEN_EXISTING As UInteger = 3
   Protected Const FILE_ATTRIBUTE_NORMAL As UInteger = &H80
   Protected INVALID_HANDLE_VALUE As New IntPtr(-1)
   Private Const INVALID_FILE_SIZE As Integer = &HFFFFFFFF
   
   ' Define Windows APIs.
   Protected Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (
                                         lpFileName As String, dwDesiredAccess As UInt32, 
                                         dwShareMode As UInt32, lpSecurityAttributes As IntPtr, 
                                         dwCreationDisposition As UInt32, dwFlagsAndAttributes As UInt32, 
                                         hTemplateFile As IntPtr) As IntPtr
   Private Declare Function GetFileSize Lib "kernel32" (hFile As SafeFileHandle, 
                                                        ByRef lpFileSizeHigh As Integer) As Integer
    
   ' Define locals.
   Private disposed As Boolean = False
   Private safeHandle As SafeFileHandle 
   Private bufferSize As Long 
   Private upperWord As Integer
   
   Public Sub New(filename As String)
      If filename Is Nothing Then
         Throw New ArgumentNullException("The filename cannot be null.")
      Else If filename = ""
         Throw New ArgumentException("The filename cannot be an empty string.")
      End If
            
      Dim handle As IntPtr = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ,
                                        IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
                                        IntPtr.Zero)
      If handle <> INVALID_HANDLE_VALUE Then
         safeHandle = New SafeFileHandle(handle, True)
      Else
         Throw New FileNotFoundException(String.Format("Cannot open '{0}'", filename))
      End If
      
      ' Get file size.
      bufferSize = GetFileSize(safeHandle, upperWord) 
      If bufferSize = INVALID_FILE_SIZE Then
         bufferSize = -1
      Else If upperWord > 0 Then 
         bufferSize = (CLng(upperWord) << 32) + bufferSize
      End If     
   End Sub
   
   Public ReadOnly Property Size As Long
      Get
         Return bufferSize
      End Get
   End Property
   
   Public Sub Dispose() _
              Implements IDisposable.Dispose
      Dispose(True)
      GC.SuppressFinalize(Me)
   End Sub           

   Protected Overridable Sub Dispose(disposing As Boolean)
      If disposed Then Exit Sub

      ' Dispose of managed resources here.
      If disposing Then
         safeHandle.Dispose()
      End If
      
      ' Dispose of any unmanaged resources not wrapped in safe handles.
      
      disposed = True
   End Sub  
End Class

Utilisation d'un handle sécurisé pour implémenter le modèle de suppression d'une classe dérivéeUsing a safe handle to implement the dispose pattern for a derived class

L'exemple suivant illustre le modèle de suppression d'une classe dérivée, DisposableStreamResource2, qui hérite de la classe DisposableStreamResource présentée dans l'exemple précédent.The following example illustrates the dispose pattern for a derived class, DisposableStreamResource2, that inherits from the DisposableStreamResource class presented in the previous example. La classe ajoute une méthode supplémentaire, WriteFileInfo, et utilise un objet SafeFileHandle pour encapsuler le handle du fichier accessible en écriture.The class adds an additional method, WriteFileInfo, and uses a SafeFileHandle object to wrap the handle of the writable file.

using Microsoft.Win32.SafeHandles;
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;

public class DisposableStreamResource2 : DisposableStreamResource
{
   // Define additional constants.
   protected const uint GENERIC_WRITE = 0x40000000; 
   protected const uint OPEN_ALWAYS = 4;
   
   // Define additional APIs.
   [DllImport("kernel32.dll")]   
   protected static extern bool WriteFile(
                                SafeFileHandle safeHandle, string lpBuffer, 
                                int nNumberOfBytesToWrite, out int lpNumberOfBytesWritten,
                                IntPtr lpOverlapped);
   
   // Define locals.
   private bool disposed = false;
   private string filename;
   private bool created = false;
   private SafeFileHandle safeHandle;
   
   public DisposableStreamResource2(string filename) : base(filename)
   {
      this.filename = filename;
   }
   
   public void WriteFileInfo()
   { 
      if (! created) {
         IntPtr hFile = CreateFile(@".\FileInfo.txt", GENERIC_WRITE, 0, 
                                   IntPtr.Zero, OPEN_ALWAYS, 
                                   FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
         if (hFile != INVALID_HANDLE_VALUE)
            safeHandle = new SafeFileHandle(hFile, true);
         else
            throw new IOException("Unable to create output file.");

         created = true;
      }

      string output = String.Format("{0}: {1:N0} bytes\n", filename, Size);
      int bytesWritten;
      bool result = WriteFile(safeHandle, output, output.Length, out bytesWritten, IntPtr.Zero);                                     
   }

   protected override void Dispose(bool disposing)
   {
      if (disposed) return;
      
      // Release any managed resources here.
      if (disposing)
         safeHandle.Dispose();
      
      disposed = true;
      
      // Release any unmanaged resources not wrapped by safe handles here.
      
      // Call the base class implementation.
      base.Dispose(disposing);
   }
}
Imports Microsoft.Win32.SafeHandles
Imports System.IO

Public Class DisposableStreamResource2 : Inherits DisposableStreamResource
   ' Define additional constants.
   Protected Const GENERIC_WRITE As Integer = &H40000000 
   Protected Const OPEN_ALWAYS As Integer = 4
   
   ' Define additional APIs.
   Protected Declare Function WriteFile Lib "kernel32.dll" (
                              safeHandle As SafeFileHandle, lpBuffer As String, 
                              nNumberOfBytesToWrite As Integer, ByRef lpNumberOfBytesWritten As Integer,
                              lpOverlapped As Object) As Boolean
   
   ' Define locals.
   Private disposed As Boolean = False
   Private filename As String
   Private created As Boolean = False
   Private safeHandle As SafeFileHandle
   
   Public Sub New(filename As String)
      MyBase.New(filename)
      Me.filename = filename
   End Sub
   
   Public Sub WriteFileInfo() 
      If Not created Then
         Dim hFile As IntPtr = CreateFile(".\FileInfo.txt", GENERIC_WRITE, 0, 
                                          IntPtr.Zero, OPEN_ALWAYS, 
                                          FILE_ATTRIBUTE_NORMAL, IntPtr.Zero)
         If hFile <> INVALID_HANDLE_VALUE Then
            safeHandle = New SafeFileHandle(hFile, True)
         Else
            Throw New IOException("Unable to create output file.")
         End If
         created = True
      End If
      Dim output As String = String.Format("{0}: {1:N0} bytes {2}", filename, Size, 
                                           vbCrLf)
      WriteFile(safeHandle, output, output.Length, 0&, Nothing)                                     
   End Sub

   Protected Overloads Overridable Sub Dispose(disposing As Boolean)
      If disposed Then Exit Sub
      
      ' Release any managed resources here.
      If disposing Then
         safeHandle.Dispose()
      End If
      
      disposed = True
      ' Release any unmanaged resources not wrapped by safe handles here.
      
      ' Call the base class implementation.
      MyBase.Dispose(disposing)
   End Sub
End Class

Voir aussiSee also