Implémentation d'une méthode Dispose

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. Le modèle de suppression est utilisé uniquement pour les objets qui accèdent à des ressources non managées. Cela est dû au fait que le garbage collector est très efficace pour récupérer les objets managés inutilisés.

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.

Il n'y a aucun avantage en matière de performances à implémenter la méthode Dispose sur des types qui utilisent uniquement des ressources managées (telles que des tableaux), car ils sont récupérés automatiquement par le garbage collector. Utilisez la méthode Dispose principalement sur les objets managés qui utilisent des ressources natives et sur les objets COM exposés au .NET Framework. Les objets managés qui utilisent des ressources natives (telles que la classe FileStream) implémentent l'interface IDisposable.

Remarque importanteImportant

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 SuppressFinalize pour l'objet qu'il supprime. Si l'objet figure actuellement dans la file d'attente de finalisation, SuppressFinalize empêche l'appel de sa méthode Finalize. 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.

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. Il est conseillé d'appeler la méthode KeepAlive à la fin d'une méthode Dispose longue.

L'alternative de SafeHandle

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. Par conséquent, nous vous recommandons de construire des objets SafeHandle au lieu d'implémenter le modèle de suppression.

La classe SafeHandle simplifie les problèmes de durée de vie des objets en assignant et en libérant des handles sans interruption. Elle contient un finaliseur critique dont le fonctionnement pendant le déchargement d'un domaine d'application est garanti. Pour plus d'informations sur les avantages de l'utilisation d'un handle sécurisé, consultez Handles sécurisés et finalisation critique.

La classe SafeHandle dans l'espace de noms System.Runtime.InteropServices est une classe wrapper abstraite pour les handles du système d'exploitation. La dérivation de cette classe est difficile. Utilisez plutôt les classes dérivées dans l'espace de noms Microsoft.Win32.SafeHandles qui fournissent des handles sécurisés pour les éléments suivants :

  • Fichiers et canaux.

  • Vues de la mémoire.

  • Constructions de chiffrement.

  • Clés de registre.

  • Handles d'attente.

Exemple

L'exemple de code suivant illustre le modèle de design recommandé pour l'implémentation d'une méthode Dispose pour des classes qui encapsulent des ressources non managées.

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.

Imports System
Imports System.IO
Class Program

    Public Shared Sub Main()
        Try
            ' Initialize a Stream resource to pass 
            ' to the DisposableResource class.
           Console.Write("Enter filename and its path: ")
            Dim fileSpec As String = Console.ReadLine
            Dim fs As FileStream = File.OpenRead(fileSpec)
            Dim TestObj As DisposableResource = New DisposableResource(fs)

            ' Use the resource.
            TestObj.DoSomethingWithResource()

            ' Dispose theresource.
            TestObj.Dispose()

        Catch e As FileNotFoundException
            Console.WriteLine(e.Message)
        End Try
    End Sub
End Class

' This class shows how to use a disposable resource.
' The resource is first initialized and passed to
' the constructor, but it could also be
' initialized in the constructor.
' The lifetime of the resource does not 
' exceed the lifetime of this instance.
' This type does not need a finalizer because it does not
' directly create a native resource like a file handle
' or memory in the unmanaged heap.
Public Class DisposableResource
    Implements IDisposable

    Private _resource As Stream

    Private _disposed As Boolean

    ' The stream passed to the constructor
    ' must be readable and not null.
    Public Sub New(ByVal stream As Stream)
        MyBase.New()
        If (stream Is Nothing) Then
            Throw New ArgumentNullException("Stream is null.")
        End If
        If Not stream.CanRead Then
            Throw New ArgumentException("Stream must be readable.")
        End If
        _resource = stream
        Dim objTypeName As String = _resource.GetType.ToString
        _disposed = False
    End Sub

    ' Demonstrates using the resource.
    ' It must not be already disposed.
    Public Sub DoSomethingWithResource()
        If _disposed Then
            Throw New ObjectDisposedException("Resource was disposed.")
        End If

        ' Show the number of bytes.
        Dim numBytes As Integer = CType(_resource.Length, Integer)
        Console.WriteLine("Number of bytes: {0}", numBytes.ToString)
    End Sub

    Public Overloads Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)

        ' Use SupressFinalize in case a subclass
        ' of this type implements a finalizer.
        GC.SuppressFinalize(Me)
    End Sub

    Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean)
        If Not _disposed Then

            ' If you need thread safety, use a lock around these 
            ' operations, as well as in your methods that use the resource.
            If disposing Then
                If (Not (_resource) Is Nothing) Then
                    _resource.Dispose()
                End If
                Console.WriteLine("Object disposed.")
            End If

            ' Indicates that the instance has been disposed.
            _resource = Nothing
            _disposed = True
        End If
    End Sub
End Class
using System;
using System.IO;

class Program
{

    static void Main()
    {
        try
        {
            // Initialize a Stream resource to pass 
            // to the DisposableResource class.
            Console.Write("Enter filename and its path: ");
            string fileSpec = Console.ReadLine();
            FileStream fs = File.OpenRead(fileSpec);
            DisposableResource TestObj = new DisposableResource(fs);

            // Use the resource.
            TestObj.DoSomethingWithResource();

            // Dispose the resource.
            TestObj.Dispose();

        }
        catch (FileNotFoundException e)
        {
            Console.WriteLine(e.Message);
        }
    }
}


// This class shows how to use a disposable resource.
// The resource is first initialized and passed to
// the constructor, but it could also be
// initialized in the constructor.
// The lifetime of the resource does not 
// exceed the lifetime of this instance.
// This type does not need a finalizer because it does not
// directly create a native resource like a file handle
// or memory in the unmanaged heap.

public class DisposableResource : IDisposable
{

    private Stream _resource;  
    private bool _disposed;

    // The stream passed to the constructor 
    // must be readable and not null.
    public DisposableResource(Stream stream)
    {
        if (stream == null)
            throw new ArgumentNullException("Stream in null.");
        if (!stream.CanRead)
            throw new ArgumentException("Stream must be readable.");

        _resource = stream;

        _disposed = false;
    }

    // Demonstrates using the resource. 
    // It must not be already disposed.
    public void DoSomethingWithResource() {
        if (_disposed)
            throw new ObjectDisposedException("Resource was disposed.");

        // Show the number of bytes.
        int numBytes = (int) _resource.Length;
        Console.WriteLine("Number of bytes: {0}", numBytes.ToString());
    }


    public void Dispose() 
    {
        Dispose(true);

        // Use SupressFinalize in case a subclass
        // of this type implements a finalizer.
        GC.SuppressFinalize(this);      
    }

    protected virtual void Dispose(bool disposing)
    {
        // If you need thread safety, use a lock around these 
        // operations, as well as in your methods that use the resource.
        if (!_disposed)
        {
            if (disposing) {
                if (_resource != null)
                    _resource.Dispose();
                    Console.WriteLine("Object disposed.");
            }

            // Indicate that the instance has been disposed.
            _resource = null;
            _disposed = true;   
        }
    }
}

Voir aussi

Référence

SuppressFinalize

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