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

HinweisHinweis

Informationen zum Abschließen und Freigeben von Ressourcen mithilfe von C++ finden Sie unter Destructors and Finalizers in Visual C++.

Klasseninstanzen kapseln häufig die Steuerung von Ressourcen, die nicht durch die Runtime verwaltet werden, z. B. Fensterhandles (HWND), Datenbankverbindungen usw. Daher sollten Sie sowohl eine explizite als auch eine implizite Methode für die Freigabe dieser Ressourcen bereitstellen. Stellen Sie eine implizite Steuerung bereit, indem Sie die geschützte Finalize für ein Objekt implementieren (Destruktorsyntax in C# und C++). Die Garbage Collection ruft diese Methode am gleichen Punkt auf, wenn keine gültigen Verweise auf das Objekt mehr vorhanden sind.

In manchen Fällen empfiehlt es sich, den Programmierern bei der Verwendung eines Objekts die Möglichkeit zu geben, diese externen Ressourcen explizit freizugeben, bevor die Garbage Collection das Objekt freigibt. Wenn eine externe Ressource knapp ist oder die Leistung beeinträchtigt, kann eine Leistungssteigerung erzielt werden, wenn der Programmierer Ressourcen explizit freigibt, sobald sie nicht mehr verwendet werden. Um explizite Steuerung bereitzustellen, implementieren Sie die Dispose, die von IDisposable bereitgestellt wird. Der Consumer des Objekts sollte diese Methode aufrufen, wenn sie das Objekt nicht mehr verwendet. Dispose kann auch dann aufgerufen werden, wenn andere Verweise auf das Objekt aktiv sind.

Beachten Sie, dass Sie auch bei Bereitstellung einer expliziten Steuerung mithilfe von Dispose eine implizite Bereinigung mithilfe der Finalize-Methode bereitstellen müssen. Finalize bietet eine Sicherung, um permanente Ressourcenverluste zu verhindern, falls der Programmierer Dispose nicht aufruft.

Weitere Informationen über das Implementieren von Finalize und Dispose zum Bereinigen nicht verwalteter Ressourcen finden Sie unter Garbage Collection. Im Folgenden Beispiel wird das grundlegende Muster des Entwurfs zum Implementieren von Dispose. Für dieses Beispiel ist der System-Namespace erforderlich.

' Design pattern for a base class.

Public Class Base
   Implements IDisposable
    ' Field to handle multiple calls to Dispose gracefully.
    Dim disposed as Boolean = false

   ' Implement IDisposable.
   Public Overloads Sub Dispose() Implements IDisposable.Dispose
      Dispose(True)
      GC.SuppressFinalize(Me)
   End Sub

   Protected Overloads Overridable Sub Dispose(disposing As Boolean)
      If disposed = False Then
          If disposing Then
             ' Free other state (managed objects).
             disposed = True
          End If
          ' Free your own state (unmanaged objects).
          ' Set large fields to null.
      End If
   End Sub

   Protected Overrides Sub Finalize()
      ' Simply call Dispose(False).
      Dispose (False)
   End Sub
End Class

' Design pattern for a derived class.
Public Class Derived
   Inherits Base

    ' Field to handle multiple calls to Dispose gracefully.
    Dim disposed as Boolean = false

   Protected Overloads Overrides Sub Dispose(disposing As Boolean)
      If disposed = False Then
          If disposing Then
             ' Release managed resources.
          End If
          ' Release unmanaged resources.
          ' Set large fields to null.
          disposed = True
      End If
      ' Call Dispose on your base class.
      Mybase.Dispose(disposing)
   End Sub
   ' The derived class does not have a Finalize method
   ' or a Dispose method without parameters because it inherits
   ' them from the base class.
End Class
// Design pattern for a base class.
public class Base: IDisposable
{
    private bool disposed = false;

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

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // Free other state (managed objects).
            }
            // Free your own state (unmanaged objects).
            // Set large fields to null.
            disposed = true;
        }
    }

    // Use C# destructor syntax for finalization code.
    ~Base()
    {
        // Simply call Dispose(false).
        Dispose (false);
    }
}

// Design pattern for a derived class.
public class Derived: Base
{
    private bool disposed = false;

    protected override void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // Release managed resources.
            }
            // Release unmanaged resources.
            // Set large fields to null.
           // Call Dispose on your base class.
            disposed = true;
        }
        base.Dispose(disposing);
    }
    // The derived class does not have a Finalize method
    // or a Dispose method without parameters because it inherits
    // them from the base class.
}

Im Folgenden Code wird das vorhergehende Beispiel, um die verschiedene Arten anzuzeigen, die Dispose aufgerufen wird und wenn Finalize aufgerufen wird. Die Phasen des freigebenden Musters werden mit Ausgabe in der Konsole angezeigt. Die Belegung und Freigabe einer nicht verwalteten Ressource wird in der abgeleiteten Klasse behandelt.

Imports System
Imports System.Collections.Generic
Imports System.Runtime.InteropServices

' Design pattern for a base class.
Public MustInherit Class Base
    Implements IDisposable

    Private disposed as Boolean = false
    Private instName As String
    Private trackingList As List(Of Object)

    Public Sub New(instanceName As String, tracking As List(Of Object))
        MyClass.instName = instanceName
        trackingList = tracking
        trackingList.Add(Me)
    End Sub

    Public ReadOnly Property InstanceName() As String
        Get
            Return instName
        End Get
    End Property

    'Implement IDisposable.
    Public Overloads Sub Dispose() Implements IDisposable.Dispose
        Console.WriteLine(vbNewLine + "[{0}].Base.Dispose()", instName)
        Dispose(true)
        GC.SuppressFinalize(Me)
    End Sub

    Protected Overloads Overridable Sub Dispose(disposing As Boolean)
        If disposed = False Then
            If disposing Then
                ' Free other state (managed objects).
                Console.WriteLine("[{0}].Base.Dispose(true)", instName)
                trackingList.Remove(Me)
                Console.WriteLine("[{0}] Removed from tracking list: {1:x16}",
                    instanceName, MyClass.GetHashCode())
            Else
                Console.WriteLine("[{0}].Base.Dispose(false)", instName)
            End If
            disposed = True
        End If
    End Sub

    Protected Overrides Sub Finalize()
        ' Simply call Dispose(False).
        Console.WriteLine(vbNewLine + "[{0}].Base.Finalize()", instName)
        Dispose(False)
    End Sub
End Class

' Design pattern for a derived class.
Public Class Derived
   Inherits Base

    Private disposed as Boolean = false
    Private umResource As IntPtr

    Public Sub New(instanceName As String, tracking As List(Of Object))
        MyBase.New(instanceName, tracking)
        ' Save the instance name as an unmanaged resource
        umResource = Marshal.StringToCoTaskMemAuto(instanceName)
    End Sub

    Protected Overloads Overrides Sub Dispose(disposing As Boolean)
        If disposed = False Then
            If disposing Then
                Console.WriteLine("[{0}].Derived.Dispose(true)", InstanceName)
                ' Release managed resources.
            Else
                Console.WriteLine("[{0}].Derived.Dispose(false)", InstanceName)
            End If
           ' Release unmanaged resources.
            If umResource <> IntPtr.Zero
                Marshal.FreeCoTaskMem(umResource)
                Console.WriteLine("[{0}] Unmanaged memory freed at {1:x16}", _
                    InstanceName, umResource.ToInt64())
                umResource = IntPtr.Zero
            End If
            disposed = True
        End If
        ' Call Dispose in the base class.
        MyBase.Dispose(disposing)
    End Sub
    ' The derived class does not have a Finalize method
    ' or a Dispose method without parameters because it inherits
    ' them from the base class.
End Class

Public Class TestDisposal
    Public Shared Sub Main()
        Dim tracking As New List(Of Object)()

        ' Dispose is not called, Finalize will be called later.
        Using Nothing
            Console.WriteLine(vbNewLine + "Disposal Scenario: #1" + vbNewLine)
            Dim d3 As New Derived("d1", tracking)
        End Using

        ' Dispose is implicitly called in the scope of the using statement.
        Using d1 As New Derived("d2", tracking)
            Console.WriteLine(vbNewLine + "Disposal Scenario: #2" + vbNewLine)
        End Using

        ' Dispose is explicitly called.
        Using Nothing
            Console.WriteLine(vbNewLine + "Disposal Scenario: #3" + vbNewLine)
            Dim d2 As New Derived("d3", tracking)
            d2.Dispose()
        End Using

        ' Again, Dispose is not called, Finalize will be called later.
        Using Nothing
            Console.WriteLine(vbNewLine + "Disposal Scenario: #4" + vbNewLine)
            Dim d4 As New Derived("d4", tracking)
        End Using

        ' List the objects remaining to dispose.
        Console.WriteLine(vbNewLine + "Objects remaining to dispose = {0:d}", tracking.Count)
        For Each dd As Derived in tracking
            Console.WriteLine("    Reference Object: {0:s}, {1:x16}",
                dd.InstanceName, dd.GetHashCode())
        Next dd
        ' Queued finalizers will be exeucted when Main() goes out of scope.
        Console.WriteLine(vbNewLine + "Dequeueing finalizers...")
    End Sub
End Class

' The program will display output similar to the following:
'
' Disposal Scenario: #1
'
'
' Disposal Scenario: #2
'
'
' [d2].Base.Dispose()
' [d2].Derived.Dispose(true)
' [d2] Unmanaged memory freed at 00000000001ce420
' [d2].Base.Dispose(true)
' [d2] Removed from tracking list: 0000000002bf8098
'
' Disposal Scenario: #3
'
'
' [d3].Base.Dispose()
' [d3].Derived.Dispose(true)
' [d3] Unmanaged memory freed at 00000000001ce420
' [d3].Base.Dispose(true)
' [d3] Removed from tracking list: 0000000000bb8560
'
' Disposal Scenario: #4
'
'
' Objects remaining to dispose = 2
'     Reference Object: d1, 000000000297b065
'     Reference Object: d4, 0000000003553390
'
' Dequeueing finalizers...
'
' [d4].Base.Finalize()
' [d4].Derived.Dispose(false)
' [d4] Unmanaged memory freed at 00000000001ce420
' [d4].Base.Dispose(false)
'
' [d1].Base.Finalize()
' [d1].Derived.Dispose(false)
' [d1] Unmanaged memory freed at 00000000001ce3f0
' [d1].Base.Dispose(false)
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

// Design pattern for a base class.
public abstract class Base : IDisposable
{
    private bool disposed = false;
    private string instanceName;
    private List<object> trackingList;

    public Base(string instanceName, List<object> tracking)
    {
        this.instanceName = instanceName;
         trackingList = tracking;
         trackingList.Add(this);
    }

    public string InstanceName
    {
        get
        {
            return instanceName;
        }
    }

    //Implement IDisposable.
    public void Dispose()
    {
        Console.WriteLine("\n[{0}].Base.Dispose()", instanceName);
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // Free other state (managed objects).
                Console.WriteLine("[{0}].Base.Dispose(true)", instanceName);
                trackingList.Remove(this);
                Console.WriteLine("[{0}] Removed from tracking list: {1:x16}",
                    instanceName, this.GetHashCode());
            }
            else
            {
                Console.WriteLine("[{0}].Base.Dispose(false)", instanceName);
            }
            disposed = true;
        }
    }

    // Use C# destructor syntax for finalization code.
    ~Base()
    {
        // Simply call Dispose(false).
        Console.WriteLine("\n[{0}].Base.Finalize()", instanceName);
        Dispose(false);
    }
}

// Design pattern for a derived class.
public class Derived : Base
{
    private bool disposed = false;
    private IntPtr umResource;

    public Derived(string instanceName, List<object> tracking) :
        base(instanceName, tracking)
    {
         // Save the instance name as an unmanaged resource
         umResource = Marshal.StringToCoTaskMemAuto(instanceName);
    }

    protected override void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                Console.WriteLine("[{0}].Derived.Dispose(true)", InstanceName);
                // Release managed resources.
            }
            else
            {
                Console.WriteLine("[{0}].Derived.Dispose(false)", InstanceName);
            }
            // Release unmanaged resources.
            if (umResource != IntPtr.Zero)
            {
                Marshal.FreeCoTaskMem(umResource);
                Console.WriteLine("[{0}] Unmanaged memory freed at {1:x16}",
                    InstanceName, umResource.ToInt64());
                umResource = IntPtr.Zero;
            }
            disposed = true;
        }
        // Call Dispose in the base class.
        base.Dispose(disposing);
    }
    // The derived class does not have a Finalize method
    // or a Dispose method without parameters because it inherits
    // them from the base class.
}

public class TestDisposal
{
    public static void Main()
    {
        List<object> tracking = new List<object>();

        // Dispose is not called, Finalize will be called later.
        using (null)
        {
            Console.WriteLine("\nDisposal Scenario: #1\n");
            Derived d3 = new Derived("d1", tracking);
        }

        // Dispose is implicitly called in the scope of the using statement.
        using (Derived d1 = new Derived("d2", tracking))
        {
            Console.WriteLine("\nDisposal Scenario: #2\n");
        }

        // Dispose is explicitly called.
        using (null)
        {
            Console.WriteLine("\nDisposal Scenario: #3\n");
            Derived d2 = new Derived("d3", tracking);
            d2.Dispose();
        }

        // Again, Dispose is not called, Finalize will be called later.
        using (null)
        {
            Console.WriteLine("\nDisposal Scenario: #4\n");
            Derived d4 = new Derived("d4", tracking);
        }

        // List the objects remaining to dispose.
        Console.WriteLine("\nObjects remaining to dispose = {0:d}", tracking.Count);
        foreach (Derived dd in tracking)
        {
            Console.WriteLine("    Reference Object: {0:s}, {1:x16}",
                dd.InstanceName, dd.GetHashCode());
        }

        // Queued finalizers will be exeucted when Main() goes out of scope.
        Console.WriteLine("\nDequeueing finalizers...");
    }
}

// The program will display output similar to the following:
//
// Disposal Scenario: #1
//
//
// Disposal Scenario: #2
//
//
// [d2].Base.Dispose()
// [d2].Derived.Dispose(true)
// [d2] Unmanaged memory freed at 000000000034e420
// [d2].Base.Dispose(true)
// [d2] Removed from tracking list: 0000000002bf8098
//
// Disposal Scenario: #3
//
//
// [d3].Base.Dispose()
// [d3].Derived.Dispose(true)
// [d3] Unmanaged memory freed at 000000000034e420
// [d3].Base.Dispose(true)
// [d3] Removed from tracking list: 0000000000bb8560
//
// Disposal Scenario: #4
//
//
// Objects remaining to dispose = 2
//    Reference Object: d1, 000000000297b065
//    Reference Object: d4, 0000000003553390
//
// Dequeueing finalizers...
//
// [d4].Base.Finalize()
// [d4].Derived.Dispose(false)
// [d4] Unmanaged memory freed at 000000000034e420
// [d4].Base.Dispose(false)
//
// [d1].Base.Finalize()
// [d1].Derived.Dispose(false)
// [d1] Unmanaged memory freed at 000000000034e3f0
// [d1].Base.Dispose(false)

Ein weiteres Codebeispiel, das das Entwurfsmuster zum Implementieren von Finalize und Dispose finden Sie unter Implementing a Dispose Method.

Anpassen des Namens einer Dispose-Methode

In bestimmten Fällen ist ein domänenspezifischer Name dem Namen Dispose vorzuziehen. So kann z. B. für eine Dateikapselung der Methodenname Close erforderlich sein. Implementieren Sie in diesem Fall Dispose privat, und erstellen Sie eine öffentliche Close-Methode, die Dispose aufruft. Das folgende Codebeispiel veranschaulicht dieses Schema. Sie können Close durch einen für Ihre Domäne geeigneten Methodennamen ersetzen. Für dieses Beispiel ist der System-Namespace erforderlich.

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

Finalize

Die folgenden Regeln dienen als Richtlinie für die Verwendung der Finalize-Methode:

  • Implementieren Sie Finalize nur für Objekte, die einen Abschluss erfordern. Finalize-Methoden wirken sich auf die Leistung aus.

  • Wenn eine Finalize-Methode erforderlich ist, empfiehlt sich möglicherweise die Implementierung von IDisposable, damit die Benutzer der Klasse nicht erst die Finalize-Methode aufrufen müssen.

  • Erhöhen Sie nicht die Sichtbarkeit der Finalize-Methode. Die Methode sollte protected sein, nicht public.

  • Die Finalize-Methode eines Objekts sollte alle externen Ressourcen freigeben, derern Eigentümer das Objekt ist. Außerdem sollte eine Finalize-Methode nur die Ressourcen freigeben, die von dem Objekt verwendet werden. Die Finalize-Methode sollte nicht auf andere Objekte verweisen.

  • Rufen Sie eine Finalize-Methode nicht direkt für ein anderes Objekt als für die Basisklasse des Objekts auf. In der Programmiersprache C# ist dies eine ungültige Operation.

  • Rufen Sie die Finalize-Methode der Basisklasse in der Finalize-Methode eines Objekts auf.

    HinweisHinweis

    Mit der C#- und C++-Destruktorsyntax wird die Finalize-Methode der Basisklasse automatisch aufgerufen.

Dispose

Die folgenden Regeln bieten Richtlinien für die Verwendung der Dispose-Methode:

  • Implementieren Sie das Dispose-Entwurfsmuster für einen Typ, der Ressourcen kapselt, die explizit freigegeben werden müssen. Die Benutzer können externe Ressourcen freigeben, indem sie die öffentliche Dispose-Methode aufrufen.

  • Implementieren Sie das Dispose-Entwurfsmuster für einen Basistyp, der normalerweise abgeleitete Typen aufweist, die Ressourcen verwenden, auch wenn der Basistyp keine Ressourcen verwendet. Wenn der Basistyp über eine Close-Methode verfügt, weist dies häufig darauf hin, dass eine Dispose-Methode implementiert werden muss. Implementieren Sie in solchen Fällen keine Finalize-Methode für den Basistyp. Finalize sollte für abgeleitete Typen implementiert werden, die Ressourcen einfügen, die eine Bereinigung erforderlich machen.

  • Geben Sie alle freigebbaren Ressourcen, deren Eigentümer ein Typ ist, in der zugehörigen Dispose-Methode frei.

  • Unterbinden Sie nach dem Aufruf von Dispose für eine Instanz die Ausführung der Finalize-Methode, indem Sie die GC.SuppressFinalize aufrufen. Eine Ausnahme zu dieser Regel ist der seltene Fall, dass Finalize Operationen ausführen muss, die durch Dispose nicht abgedeckt sind.

  • Rufen Sie die Dispose-Methode der Basisklasse auf, wenn sie IDisposable implementiert.

  • Gehen Sie nicht davon aus, dass Dispose aufgerufen wird. Nicht verwaltete Ressourcen, deren Eigentümer ein Typ ist, sollten ebenfalls in einer Finalize-Methode freigegeben werden, falls Dispose nicht aufgerufen wird.

  • Lösen Sie für diesen Typ eine ObjectDisposedException über Instanzmethoden (nicht Dispose) aus, wenn die Ressourcen bereits freigegeben wurden. Diese Regel gilt nicht für die Dispose-Methode, da diese mehrfach aufrufbar sein sollte, ohne eine Ausnahme auszulösen.

  • Geben Sie die Aufrufe von Dispose über die Hierarchie der Basistypen weiter. Die Dispose-Methode sollte alle Ressourcen freigeben, die von diesem Objekt verwendet werden, und alle Objekte, deren Eigentümer dieses Objekt ist. Sie können z. B. ein TextReader-Objekt erstellen, das einen Stream und ein Encoding verwendet, die beide ohne Wissen des Benutzers durch den TextReader erstellt werden. Darüber hinaus können sowohl Stream als auch Encoding externe Ressourcen abrufen. Wenn Sie die Dispose-Methode für den TextReader aufrufen, sollte sie wiederum die Dispose-Methode für Stream und Encoding aufrufen, sodass diese ihre externen Ressourcen freigeben.

  • Es empfiehlt sich u. U. festzulegen, dass ein Objekt nicht mehr verwendet werden kann, nachdem seine Dispose-Methode aufgerufen wurde. Die Neuerstellung eines Objekts, das bereits freigegeben wurde, ist ein schwieriges Implementierungsproblem.

  • Lassen Sie zu, dass eine Dispose-Methode mehrmals aufgerufen werden kann, ohne dass eine Ausnahme ausgelöst wird. Nach dem ersten Aufruf sollte die Methode keine Operation ausführen.

Copyright für einzelne Teile 2005 Microsoft Corporation. Alle Rechte vorbehalten.

Copyright für einzelne Teile Addison-Wesley Corporation. Alle Rechte vorbehalten.

Weitere Informationen zu Entwurfsrichtlinien finden Sie unter „Framework-Entwurfs-Richtlinien: Idiome, Konventionen und Muster für wiederverwendbare .NET-Bibliotheken von Krzysztof Cwalina“ book und Brad Abrams, veröffentlicht von Addison-Wesley, 2005.

Siehe auch

Referenz

IDisposable.Dispose

Object.Finalize

Weitere Ressourcen

Entwurfsrichtlinien zum Entwickeln von Klassenbibliotheken

Garbage Collection

Entwurfsmuster