Freigeben über


Implementieren einer Dispose-Methode

Das Muster zum Verwerfen eines Objekts, Dispose-Muster genannt, legt eine Ordnung für die Lebensdauer von Objekten fest. Das Dispose-Muster wird nur für Objekte verwendet, die auf nicht verwaltete Ressourcen zugreifen. Dies liegt daran, dass der Garbage Collector beim Freigeben nicht verwendeter verwalteter Objekte sehr effizient ist.

Die Dispose-Methode eines Typs sollte alle Ressourcen freigeben, die dieser besitzt. Sie sollte außerdem alle Ressourcen freigeben, deren Eigentümer die Basistypen der Methode sind, indem die Dispose-Methode des übergeordneten Typs aufgerufen wird. Die Dispose-Methode des übergeordneten Typs sollte alle Ressourcen freigeben, die dieser besitzt, und dann die Dispose-Methode seines übergeordneten Typs aufrufen. Dieses Muster wird durch die Hierarchie der Basistypen weitergegeben. Um sicherzustellen, dass Ressourcen immer entsprechend bereinigt werden, sollte eine Dispose-Methode auch mehrmals aufgerufen werden können, ohne eine Ausnahme auszulösen.

Durch das Implementieren der Dispose-Methode für Typen, die nur verwaltete Ressourcen (z. B. Arrays) verwenden, werden keine Leistungsvorteile erzielt, da diese automatisch vom Garbage Collector freigegeben werden. Verwenden Sie die Dispose-Methode primär für verwaltete Objekte, die systemeigene Ressourcen verwenden, und für COM-Objekte, die für .NET Framework verfügbar sind. Verwaltete Objekte, die systemeigene Ressourcen (z. B. die FileStream-Klasse) verwenden, implementieren die IDisposable-Schnittstelle.

Wichtiger HinweisWichtig

C++-Programmierer sollten dieses Thema nicht verwenden.Stattdessen sollten Sie Destructors and Finalizers in Visual C++ lesen.In .NET Framework, Version 2.0, bietet der C++-Compiler Unterstützung, um das deterministische Verwerfen von Ressourcen zu implementieren. Die direkte Implementierung der Dispose-Methode wird nicht unterstützt.

Eine Dispose-Methode sollte die SuppressFinalize-Methode für das Objekt aufrufen, das es freigibt. Wenn sich das Objekt gegenwärtig in der Finalisierungswarteschlange befindet, verhindert SuppressFinalize, dass dessen Finalize-Methode aufgerufen wird. Beachten Sie, dass das Ausführen einer Finalize-Methode hohen Leistungsaufwand erfordert. Wenn Sie das Objekt bereits mit der Dispose-Methode bereinigt haben, muss die Finalize-Methode des Objekts nicht mehr vom Garbage Collector aufgerufen werden.

Das Codebeispiel für die GC.KeepAlive-Methode veranschaulicht, wie durch agressive Garbage Collection die Ausführung eines Finalizers bewirkt werden kann, während ein Member des freigegebenen Objekts noch ausgeführt wird. Es empfiehlt sich, die KeepAlive-Methode am Ende einer längeren Dispose-Methode aufzurufen.

Die SafeHandle-Alternative

Das Schreiben von Code für den Finalizer eines Objekts ist eine komplexe Aufgabe, die Probleme verursachen kann, wenn sie nicht ordnungsgemäß gelöst wird. Daher wird empfohlen, SafeHandle-Objekte zu erstellen, anstatt das Dispose-Muster zu implementieren.

Die SafeHandle-Klasse vereinfacht Probleme mit der Objektlebensdauer, indem sie Handles ohne Unterbrechung zuweist und freigibt. Sie enthält einen kritischen Finalizer, der auf jeden Fall ausgeführt wird, während eine Anwendungsdomäne entladen wird. Weitere Informationen zu den Vorteilen bei der Verwendung von SafeHandle finden Sie unter Sichere Handles und entscheidendes Abschließen.

Die SafeHandle-Klasse im System.Runtime.InteropServices-Namespace ist eine abstrakte Wrapperklasse für Betriebssystemhandles. Das Ableiten von dieser Klasse ist schwierig. Verwenden Sie stattdessen die abgeleiteten Klassen im Microsoft.Win32.SafeHandles-Namespace, die sichere Handles für Folgendes bereitstellen:

  • Dateien und Pipes.

  • Speicheransichten.

  • Kryptografiekonstrukte.

  • Registrierungsschlüssel.

  • Wait-Handles.

Beispiel

Im folgenden Codebeispiel wird das empfohlene Entwurfsmuster für das Implementieren einer Dispose-Methode für Klassen veranschaulicht, die nicht verwaltete Ressourcen kapseln.

Ressourcenklassen werden i. d.R. von komplexen systemeigenen Klassen oder APIs abgeleitet und müssen entsprechend angepasst werden. Verwenden Sie dieses Codemuster als Anfangspunkt zum Erstellen einer Ressourcenklasse, und stellen Sie die erforderliche Anpassung basierend auf den Ressourcen, die Sie kapseln, bereit.

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

Siehe auch

Referenz

SuppressFinalize

Destructors and Finalizers in Visual C++

Implementieren der Methoden "Finalize" und "Dispose" zum Bereinigen von nicht verwalteten Ressourcen

Konzepte

Überschreiben der Finalize-Methode