Implémenter une méthode DisposeImplement a Dispose method

L’implémentation de la Dispose méthode est principalement destinée à libérer des ressources non managées.Implementing the Dispose method is primarily for releasing unmanaged resources. Lors de l’utilisation de membres d’instance qui sont des IDisposable implémentations, il est courant d’appeler en cascade Dispose .When working with instance members that are IDisposable implementations, it's common to cascade Dispose calls. Il existe des raisons supplémentaires d’implémenter Dispose , par exemple, pour libérer de la mémoire qui a été allouée, supprimer un élément qui a été ajouté à une collection ou signaler la libération d’un verrou acquis.There are additional reasons for implementing Dispose, for example, to free memory that was allocated, remove an item that was added to a collection, or signal the release of a lock that was acquired.

Le garbage collector .net n’alloue pas ou ne libère pas de mémoire non managée.The .NET garbage collector does not allocate or release unmanaged memory. Le modèle de suppression d’un objet, appelé 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 the dispose pattern, imposes order on the lifetime of an object. Le modèle de suppression est utilisé pour les objets qui implémentent l' IDisposable interface et est courant lors de l’interaction avec les handles de fichiers et de canaux, les handles de Registre, les handles d’attente ou les pointeurs vers des blocs de mémoire non managée.The dispose pattern is used for objects that implement the IDisposable interface, and is common when interacting with file and pipe handles, registry handles, wait handles, or pointers to blocks of unmanaged memory. Cela est dû au fait que le garbage collector ne peut pas récupérer les objets non managés.This is because the garbage collector is unable to reclaim unmanaged objects.

Pour garantir que les ressources sont toujours nettoyées de manière appropriée, une Dispose méthode doit être idempotent, de sorte qu’elle peut être appelée plusieurs fois sans lever d’exception.To help ensure that resources are always cleaned up appropriately, a Dispose method should be idempotent, such that it is callable multiple times without throwing an exception. En outre, les appels suivants de Dispose ne doivent rien faire.Furthermore, subsequent invocations of Dispose should do nothing.

L’exemple de code fourni pour la GC.KeepAlive méthode montre comment garbage collection peut provoquer l’exécution d’un finaliseur, alors qu’une référence non managée à l’objet ou à ses membres est toujours en cours d’utilisation.The code example provided for the GC.KeepAlive method shows how garbage collection can cause a finalizer to run, while an unmanaged reference to the object or its members is still in use. Il peut être judicieux d’utiliser GC.KeepAlive pour rendre l’objet inéligible pour garbage collection du début de la routine actuelle jusqu’au point où cette méthode est appelée.It may make sense to utilize GC.KeepAlive to make the object ineligible for garbage collection from the start of the current routine to the point where this method is called.

Handles sécurisésSafe 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.

Un System.Runtime.InteropServices.SafeHandle est un type managé abstrait qui encapsule un System.IntPtr qui identifie une ressource non managée.A System.Runtime.InteropServices.SafeHandle is an abstract managed type that wraps an System.IntPtr that identifies an unmanaged resource. Sur Windows, il peut identifier un handle sur UNIX, un descripteur de fichier.On Windows it might identify a handle while on Unix, a file descriptor. Il fournit toute la logique nécessaire pour s’assurer que cette ressource est libérée une seule fois, lorsque la SafeHandle est supprimée de ou lorsque toutes les références à SafeHandle ont été supprimées et que l' SafeHandle instance est finalisée.It provides all of the logic necessary to ensure that this resource is released once and only once, when the SafeHandle is disposed of or when all references to the SafeHandle have been dropped and the SafeHandle instance is finalized.

System.Runtime.InteropServices.SafeHandleEst une classe de base abstraite.The System.Runtime.InteropServices.SafeHandle is an abstract base class. Les classes dérivées fournissent des instances spécifiques pour différents genres de handles.Derived classes provide specific instances for different kinds of handle. Ces classes dérivées valident les valeurs de qui System.IntPtr sont considérées comme non valides et comment libérer le handle.These derived classes validate what values for the System.IntPtr are considered invalid and how to actually free the handle. Par exemple, SafeFileHandle dérive de SafeHandle à Wrap IntPtrs qui identifient les descripteurs/descripteurs de fichiers ouverts et substitue sa SafeHandle.ReleaseHandle() méthode pour le fermer (via la close fonction sur UNIX ou CloseHandle Function sur Windows).For example, SafeFileHandle derives from SafeHandle to wrap IntPtrs that identify open file handles/descriptors, and overrides its SafeHandle.ReleaseHandle() method to close it (via the close function on Unix or CloseHandle function on Windows). La plupart des API dans les bibliothèques .NET qui créent une ressource non managée l’encapsulent dans un SafeHandle et le renvoient en SafeHandle fonction des besoins, plutôt que de transmettre le pointeur brut.Most APIs in .NET libraries that create an unmanaged resource will wrap it in a SafeHandle and return that SafeHandle to you as needed, rather than handing back the raw pointer. Dans les situations où vous interagissez avec un composant non managé et récupérez un IntPtr pour une ressource non managée, vous pouvez créer votre propre SafeHandle type pour l’encapsuler.In situations where you interact with an unmanaged component and get an IntPtr for an unmanaged resource, you can create your own SafeHandle type to wrap it. Par conséquent, peu de types n' SafeHandle ont pas besoin d’implémenter des finaliseurs ; la plupart des implémentations de modèle jetables finissent par encapsuler d’autres ressources managées, dont certaines peuvent être des SafeHandle s.As a result, few non-SafeHandle types need to implement finalizers; most disposable pattern implementations only end up wrapping other managed resources, some of which may be SafeHandles.

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:

Dispose () et dispose (bool)Dispose() and Dispose(bool)

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. En outre, toute classe non scellée doit avoir une Dispose(bool) méthode de surcharge supplémentaire à implémenter :Also, any non-sealed class should have an additional Dispose(bool) overload method to be implemented:

  • publicImplémentation non virtuelle ( 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.

  • protected virtualMéthode ( 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)
    End Sub
    

    Important

    Le disposing paramètre doit être false lorsqu’il est appelé à partir d’un finaliseur, et lorsqu’il est true appelé à partir de la IDisposable.Dispose méthode.The disposing parameter should be false when called from a finalizer, and true when called from the IDisposable.Dispose method. En d’autres termes, il est quand il est appelé de façon true déterministe et lorsqu’il n’est false pas appelé de manière déterministe.In other words, it is true when deterministically called and false when non-deterministically called.

La méthode Dispose ()The Dispose() method

Étant donné que le public , non virtuel ( NonInheritable en Visual Basic), la Dispose méthode sans paramètre est appelée par un consommateur du type, son objectif est de libérer des ressources non managées, d’effectuer un nettoyage général 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, perform general cleanup, and to indicate that the finalizer, if one is present, doesn't have to run. La libération de la mémoire réelle associée à un objet géré est toujours le domaine du garbage collector.Freeing the actual memory associated with a managed object is always the domain of the garbage collector. 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 nettoyage réel est effectué par la Dispose(bool) surcharge de méthode.Note that the actual cleanup is performed by the Dispose(bool) method overload.

Surcharge de la méthode Dispose (bool)The Dispose(bool) method overload

Dans la surcharge, le disposing paramètre est un Boolean qui indique si l’appel de méthode provient d’une Dispose méthode (sa valeur est true ) ou d’un finaliseur (sa valeur est false ).In the 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 Dispose implémentation (dispose en cascade).The conditional block can be used to call their Dispose implementation (cascade dispose). Si vous avez utilisé une classe dérivée de System.Runtime.InteropServices.SafeHandle pour encapsuler votre ressource non managée, vous devez appeler l' SafeHandle.Dispose() implémentation ici.If you have used a derived class of System.Runtime.InteropServices.SafeHandle to wrap your unmanaged resource, you should call the SafeHandle.Dispose() 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. Affectez des références d’objets managés volumineux à null pour les rendre plus susceptibles d’être inaccessibles.Assign large managed object references to null to make them more likely to be unreachable. Cela les libère plus rapidement que s’ils étaient récupérés de façon non déterministe.This releases them faster than if they were reclaimed non-deterministically.

Si l’appel de méthode provient d’un finaliseur, seul le code qui libère les ressources non managées doit s’exécuter.If the method call comes from a finalizer, only the code that frees unmanaged resources should execute. L’implémenteur est chargé de s’assurer que le chemin d’accès faux n’interagit pas avec les objets managés qui ont pu être récupérés.The implementer is responsible for ensuring the the false path doesn't interact with managed objects that may have been reclaimed. Cela est important, car l’ordre dans lequel le garbage collector détruit les objets managés pendant la finalisation n’est pas déterministe.This is important because the order in which the garbage collector destroys managed objects during finalization is non-deterministic.

Appels de suppression en cascadeCascade dispose calls

Si votre classe possède un champ ou une propriété et que son type implémente IDisposable , la classe conteneur elle-même doit également implémenter IDisposable .If your class owns a field or property, and its type implements IDisposable, the containing class itself should also implement IDisposable. Une classe qui instancie une IDisposable implémentation et la stocke en tant que membre d’instance est également responsable de son nettoyage.A class that instantiates an IDisposable implementation and storing it as an instance member, is also responsible for its cleanup. Cela permet de s’assurer que les types jetables référencés ont la possibilité de procéder à un nettoyage déterministe à l’aide de la Dispose méthode.This is to help ensure that the referenced disposable types are given the opportunity to deterministically perform clean up through the Dispose method. Dans cet exemple, la classe est sealed (ou NotInheritable dans Visual Basic).In this example, the class is sealed (or NotInheritable in Visual Basic).

public sealed class Foo : IDisposable
{
    private readonly IDisposable _bar;

    public Foo()
    {
        _bar = new Bar();
    }

    public void Dispose()
    {
        _bar?.Dispose();
    }
}
Public NotInheritable Class Foo
    Implements IDisposable

    Private ReadOnly _bar As IDisposable

    Public Sub New()
        _bar = New Bar()
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
        _bar.Dispose()
    End Sub
End Class

Implémenter le modèle de suppressionImplement the dispose pattern

Toutes les classes non-sealed ou (classes Visual Basic non modifiées comme NotInheritable ) doivent être considérées comme une classe de base potentielle, car elles peuvent être héritées.All non-sealed classes or (Visual Basic classes not modified as NotInheritable) should be considered a potential base class, because they could be inherited. Si vous implémentez le modèle de suppression pour une classe de base potentielle quelconque, vous devez fournir les éléments suivants :If you implement the dispose pattern for any potential base class, you must provide the following:

  • Une implémentation de Dispose qui appelle la méthode Dispose(bool).A Dispose implementation that calls the Dispose(bool) method.
  • Dispose(bool)Méthode qui effectue le nettoyage réel.A Dispose(bool) method that performs the actual cleanup.
  • 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 SafeHandle classe fournit un finaliseur, donc vous n’avez pas besoin d’en écrire un vous-même.The SafeHandle class provides a finalizer, so you do not have to write one yourself.

Important

Une classe de base peut uniquement référencer des objets managés et implémenter le modèle de suppression.It is possible for a base class to only reference managed objects, and implement the dispose pattern. Dans ce cas, un finaliseur n’est pas nécessaire.In these cases, a finalizer is unnecessary. Un finaliseur est requis uniquement si vous référencez directement des ressources non managées.A finalizer is only required if you directly reference unmanaged resources.

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
{
    // To detect redundant calls
    private bool _disposed = false;

    // Instantiate a SafeHandle instance.
    private SafeHandle _safeHandle = new SafeFileHandle(IntPtr.Zero, true);

    // Public implementation of Dispose pattern callable by consumers.
    public void Dispose() => Dispose(true);

    // Protected implementation of Dispose pattern.
    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
        {
            return;
        }

        if (disposing)
        {
           // Dispose managed state (managed objects).
            _safeHandle?.Dispose();
        }

        _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
{
    // To detect redundant calls
    private bool _disposed = false;

    ~BaseClass() => Dispose(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)
        {
            // TODO: dispose managed state (managed objects).
        }

        // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
        // TODO: set large fields to null.

        _disposed = true;
    }
}
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

Conseil

En C#, vous créez un finaliseur en substituant Object.Finalize .In C#, you create a finalizer by overriding Object.Finalize. Dans Visual Basic, cette opération s’effectue à l’aide de Protected Overrides Sub Finalize() .In Visual Basic, this is done with Protected Overrides Sub Finalize().

Implémenter le modèle de suppression d’une classe dérivéeImplement 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. Au lieu de cela, pour nettoyer une classe dérivée, vous devez fournir les éléments suivants :Instead, to cleanup a derived class, you provide the following:

  • protected override void Dispose(bool)Méthode qui remplace la méthode de la classe de base et effectue le nettoyage réel de la classe dérivée.A protected override void Dispose(bool) method that overrides the base class method and performs the actual cleanup of the derived class. Cette méthode doit également appeler la base.Dispose(bool) MyBase.Dispose(bool) méthode (en Visual Basic) de la classe de base et passer son état de suppression pour l’argument.This method must also call the base.Dispose(bool) (MyBase.Dispose(bool) in Visual Basic) 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 Dispose(bool) surcharge avec un disposing argument de false .If you do provide a finalizer, it must call the Dispose(bool) 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
{
    // To detect redundant calls
    private bool _disposed = false;

    // Instantiate a SafeHandle instance.
    private SafeHandle _safeHandle = new SafeFileHandle(IntPtr.Zero, true);

    // Protected implementation of Dispose pattern.
    protected override void Dispose(bool disposing)
    {
        if (_disposed)
        {
            return;
        }

        if (disposing)
        {
           // Dispose managed state (managed objects).
            _safeHandle?.Dispose();
        }

        _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
{
    // To detect redundant calls
    bool _disposed = false;

    ~DerivedClass() => Dispose(false);

    // Protected implementation of Dispose pattern.
    protected override void Dispose(bool disposing)
    {
        if (_disposed)
        {
            return;
        }

        if (disposing)
        {
            // TODO: dispose managed state (managed objects).
        }

        // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
        // TODO: set large fields to null.
        _disposed = true;

        // Call the base class implementation.
        base.Dispose(disposing);
    }
}
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

Implémenter le modèle de suppression avec des Handles sécurisésImplement the dispose pattern with safe handles

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 DisposableStreamResource qui utilise SafeFileHandle pour encapsuler un objet Stream qui représente un fichier ouvert.It defines a DisposableStreamResource class that uses a SafeFileHandle to wrap a Stream object that represents an open file. La classe comprend également une seule propriété, Size , qui retourne le nombre total d’octets dans le flux de fichier.The class 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.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;
    private const int INVALID_FILE_SIZE = unchecked((int)0xFFFFFFFF);

    // Define Windows APIs.
    [DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode)]
    protected static extern SafeFileHandle 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 readonly SafeFileHandle _safeHandle;
    private readonly int _upperWord;

    public DisposableStreamResource(string fileName)
    {
        if (string.IsNullOrWhiteSpace(fileName))
        {
            throw new ArgumentException("The fileName cannot be null or an empty string");
        }

        _safeHandle = CreateFile(
            fileName, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero,
            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);

        // Get file size.
        Size = GetFileSize(_safeHandle, out _upperWord);
        if (Size == INVALID_FILE_SIZE)
        {
            Size = -1;
        }
        else if (_upperWord > 0)
        {
            Size = (((long)_upperWord) << 32) + Size;
        }
    }

    public long Size { get; }

    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 = &H0I
    Protected Const OPEN_EXISTING As UInteger = 3
    Protected Const FILE_ATTRIBUTE_NORMAL As UInteger = &H80
    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 SafeFileHandle

    Private Declare Function GetFileSize Lib "kernel32" (
        hFile As SafeFileHandle, ByRef lpFileSizeHigh As Integer) As Integer

    ' Define locals.
    Private disposed As Boolean = False
    Private ReadOnly safeHandle As SafeFileHandle
    Private ReadOnly upperWord As Integer

    Public Sub New(fileName As String)
        If String.IsNullOrWhiteSpace(fileName) Then
            Throw New ArgumentNullException("The fileName cannot be null or an empty string")
        End If

        safeHandle = CreateFile(
            fileName, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero,
            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero)

        ' Get file size.
        Size = GetFileSize(safeHandle, upperWord)
        If Size = INVALID_FILE_SIZE Then
            Size = -1
        ElseIf upperWord > 0 Then
            Size = (CLng(upperWord) << 32) + Size
        End If
    End Sub

    Public ReadOnly Property Size As Long

    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

Implémenter le modèle de suppression d’une classe dérivée avec des Handles sécurisésImplement the dispose pattern for a derived class with safe handles

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.Runtime.InteropServices;

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);

    // To detect redundant calls
    private bool _disposed = false;
    private bool _created = false;
    private SafeFileHandle _safeHandle;
    private readonly string _fileName;

    public DisposableStreamResource2(string fileName) : base(fileName) => _fileName = fileName;

    public void WriteFileInfo()
    {
        if (!_created)
        {
            _safeHandle = CreateFile(
                @".\FileInfo.txt", GENERIC_WRITE, 0, IntPtr.Zero,
                OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);

            _created = true;
        }

        string output = $"{_fileName}: {Size:N0} bytes\n";
        _ = WriteFile(_safeHandle, output, output.Length, out _, IntPtr.Zero);
    }

    protected override void Dispose(bool disposing)
    {
        if (_disposed)
        {
            return;
        }

        // Release any managed resources here.
        if (disposing)
        {
            // Dispose managed state (managed objects).
            _safeHandle?.Dispose();
        }

        // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
        // TODO: set large fields to null.

        _disposed = true;

        // 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 created As Boolean = False
    Private safeHandle As SafeFileHandle
    Private ReadOnly filename As String

    Public Sub New(filename As String)
        MyBase.New(filename)
        Me.filename = filename
    End Sub

    Public Sub WriteFileInfo()
        If Not created Then
            safeHandle = CreateFile(
                ".\FileInfo.txt", GENERIC_WRITE, 0, IntPtr.Zero,
                OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero)
            created = True
        End If

        Dim output As String = $"{filename }: {Size:N0} bytes {vbCrLf }"
        Dim result = WriteFile(safeHandle, output, output.Length, 0&, Nothing)
    End Sub

    Protected Overridable Overloads 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