Share via


Implementieren einer Dispose-Methode

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.

Wichtig

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 GC.SuppressFinalize-Methode für das freizugebende Objekt aufrufen. Wenn sich das Objekt derzeit in der Finalisierungswarteschlange befindet, verhindert GC.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.

Hinweis

Das Codebeispiel für die System.GC.KeepAlive(System.Object)-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.

Das folgende Codebeispiel soll das empfohlene Entwurfsmuster für das Implementieren einer Dispose-Methode für Klassen veranschaulichen, die nicht verwaltete Ressourcen einkapseln. Dieses Muster ist im gesamten .NET Framework implementiert.

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. Sie können dieses Beispiel nicht kompilieren und direkt in einer Anwendung verwenden.

In diesem Beispiel implementiert die BaseResource-Basisklasse eine öffentliche Dispose-Methode, die von Benutzern der Klasse aufgerufen werden kann. Diese wiederum ruft die Methode virtual Dispose(bool disposing) auf (virtual Dispose(disposing As Boolean) in Visual Basic). Je nach der Identität des Aufrufers wird true oder false übergeben. Der entsprechende Code zum Bereinigen des Objekts wird in der virtuellen Dispose-Methode ausgeführt.

Dispose(bool disposing) wird in zwei verschiedenen Szenarien ausgeführt. Wenn disposing gleich true ist , wurde die Methode entweder direkt oder indirekt durch einen Benutzercode aufgerufen, und sowohl verwaltete als auch nicht verwaltete Ressourcen können freigegeben werden. Wenn disposing gleich false ist , wurde die Methode durch die Common Language Runtime aus dem Finalizer aufgerufen, und nur nicht verwaltete Ressourcen können freigegeben werden. Wenn ein Objekt seinen Finalisierungscode ausführt, sollte es nicht auf andere Objekte verweisen, da Finalizer nicht in einer bestimmten Reihenfolge ausgeführt werden. Wenn ein ausführender Finalizer auf ein anderes Objekt verweist, das bereits finalisiert wurde, schlägt der ausführende Finalizer fehl.

Die Basisklasse stellt eine Finalize-Methode bzw. einen Destruktor als Sicherheit für den Fall bereit, dass Dispose nicht aufgerufen wird. Die Finalize-Methode ruft die Dispose-Methode auf, die Parameter akzeptiert, und übergibt dabei false. Sie sollten den Dispose-Bereinigungscode nicht innerhalb der Finalize-Methode neu erstellen. Das Aufrufen von Dispose(false) ist optimal für die Lesbarkeit und Verwaltung des Codes.

Die MyResourceWrapper-Klasse veranschaulicht das Ableiten von einer Klasse, die die Ressourcenverwaltung unter Verwendung von Dispose implementiert. MyResourceWrapper überschreibt die virtual Dispose(bool disposing)-Methode und stellt einen Bereinigungscode für die verwalteten und nicht verwalteten Ressourcen bereit, die erstellt werden. MyResourceWrapper ruft Dispose auch für seine BaseResource-Basisklasse auf, um sicherzustellen, dass die Basis ordnungsgemäß bereinigt werden kann. Beachten Sie, dass die abgeleitete MyResourceWrapper-Klasse nicht über eine Finalize-Methode oder eine Dispose-Methode ohne Parameter verfügt, da sie diese von der BaseResource-Basisklasse erbt.

Hinweis

Die protected Dispose(bool disposing)-Methode in diesem Beispiel erzwingt keine Threadsicherheit, da die Methode nicht gleichzeitig aus einem Benutzerthread und einem Finalizerthread aufgerufen werden kann. Zusätzlich sollte die Clientanwendung, die BaseResource verwendet, zu keinem Zeitpunkt den gleichzeitigen Aufruf der protected Dispose(bool disposing)-Methode durch mehrere Benutzerthreads zulassen. Eine Anwendungs- oder Klassenbibliothek sollte so entworfen werden, dass nur ein Thread die Lebensdauer einer Ressource besitzen und Dispose aufrufen kann, wenn die Ressource nicht mehr benötigt wird. In Abhängigkeit von der Ressource kann nicht synchronisierter Threadzugriff bei der Freigabe von Ressourcen ein Sicherheitsrisiko darstellen. Entwickler sollten Code sorgfältig prüfen, um die beste Vorgehensweise für das Erzwingen der Threadsicherheit zu bestimmen.

' Design pattern for the base class.
' By implementing IDisposable, you are announcing that instances
' of this type allocate scarce resources.
Public Class BaseResource
   Implements IDisposable
   ' Pointer to an external unmanaged resource.
   Private handle As IntPtr 
   ' Other managed resource this class uses.
   Private Components As Component
   ' Track whether Dispose has been called.
   Private disposed As Boolean = False

   ' Constructor for the BaseResource Object.
   Public Sub New()
      ' Insert appropriate constructor code here.
   End Sub

   ' Implement IDisposable.
   ' Do not make this method Overridable.
   ' A derived class should not be able to override this method.
   Public Overloads Sub Dispose()Implements IDisposable.Dispose
      Dispose(true)
      ' Take yourself off of the finalization queue
      ' to prevent finalization code for this object
      ' from executing a second time.
      GC.SuppressFinalize(Me) 
   End Sub

' Dispose(disposing As Boolean) executes in two distinct scenarios.
' If disposing is true, the method has been called directly 
' or indirectly by a user's code. Managed and unmanaged resources 
' can be disposed.
' If disposing equals false, the method has been called by the runtime
' from inside the finalizer and you should not reference other    
' objects. Only unmanaged resources can be disposed.
Protected Overloads Overridable Sub Dispose(disposing As Boolean)
   ' Check to see if Dispose has already been called.
   If Not (Me.disposed) Then
      ' If disposing equals true, dispose all managed 
      ' and unmanaged resources.
      If (disposing) Then
         ' Dispose managed resources.
         Components.Dispose()
      End If
      ' Release unmanaged resources. If disposing is false,
      ' only the following code is executed.      
      CloseHandle(handle)
      handle = IntPtr.Zero
      ' Note that this is not thread safe.
      ' Another thread could start disposing the object
      ' after the managed resources are disposed,
      ' but before the disposed flag is set to true.
      ' If thread safety is necessary, it must be
      ' implemented by the client.
   End If
   Me.disposed = true
End Sub

   ' This Finalize method will run only if the 
   ' Dispose method does not get called.
   ' By default, methods are NotOverridable. 
   ' This prevents a derived class from overriding this method.
   Protected Overrides Sub Finalize()
         ' Do not re-create Dispose clean-up code here.
         ' Calling Dispose(false) is optimal in terms of
         ' readability and maintainability.
         Dispose(false)
   End Sub
   
   ' Allow your Dispose method to be called multiple times,
   ' but throw an exception if the object has been disposed.
   ' Whenever you do something with this class, 
   ' check to see if it has been disposed.
   Public Sub DoSomething()
      If Me.disposed Then
         Throw New ObjectDisposedException()
      End if
   End Sub
End Class

' Design pattern for a derived class.
' Note that this derived class inherently implements the 
' IDisposable interface because it is implemented in the base class.
Public Class MyResourceWrapper
   Inherits BaseResource
   
   ' A managed resource that you add in this derived class.
   private addedManaged As ManagedResource
   ' A native unmanaged resource that you add in this derived class.
   private addedNative As NativeResource
   ' Track whether Dispose has been called.
   Private disposed As Boolean = False

   ' Constructor for the MyResourceWrapper object.
   Public Sub New()      
      MyBase.New()
      ' Insert appropriate constructor code here for the
      ' added resources.
   End Sub

   Protected Overloads Overrides Sub Dispose(disposing As Boolean)
      If Not (Me.disposed) Then
         Try
            If disposing Then
              ' Release the managed resources you added in
              ' this derived class here.
              addedManaged.Dispose()
            End If
            ' Release the native unmanaged resources you added
            ' in this derived class here.
            CloseHandle(addedNative)
            Me.disposed = true
         Finally
            ' Call Dispose on your base class.
            MyBase.Dispose(disposing)
         End Try
      End If
   End Sub
End Class
' This derived class does not have a Finalize method
' or a Dispose method without parameters because it 
' inherits them from the base class.
// Design pattern for the base class.
// By implementing IDisposable, you are announcing that instances
// of this type allocate scarce resources.
public class BaseResource: IDisposable
{
   // Pointer to an external unmanaged resource.
   private IntPtr handle;
   // Other managed resource this class uses.
   private Component Components;
   // Track whether Dispose has been called.
   private bool disposed = false;

   // Constructor for the BaseResource object.
   public BaseResource()
   {
      // Insert appropriate constructor code here.
   }

   // Implement IDisposable.
   // Do not make this method virtual.
   // A derived class should not be able to override this method.
   public void Dispose()
   {
      Dispose(true);
      // Take yourself off the Finalization queue 
      // to prevent finalization code for this object
      // from executing a second time.
      GC.SuppressFinalize(this);
   }

   // Dispose(bool disposing) executes in two distinct scenarios.
   // If disposing equals true, the method has been called directly
   // or indirectly by a user's code. Managed and unmanaged resources
   // can be disposed.
   // If disposing equals false, the method has been called by the 
   // runtime from inside the finalizer and you should not reference 
   // other objects. Only unmanaged resources can be disposed.
   protected virtual void Dispose(bool disposing)
   {
      // Check to see if Dispose has already been called.
      if(!this.disposed)
      {
         // If disposing equals true, dispose all managed 
         // and unmanaged resources.
         if(disposing)
         {
            // Dispose managed resources.
            Components.Dispose();
         }
         // Release unmanaged resources. If disposing is false, 
         // only the following code is executed.
         CloseHandle(handle);
         handle = IntPtr.Zero;
         // Note that this is not thread safe.
         // Another thread could start disposing the object
         // after the managed resources are disposed,
         // but before the disposed flag is set to true.
         // If thread safety is necessary, it must be
         // implemented by the client.

      }
      disposed = true;         
   }

   // Use C# destructor syntax for finalization code.
   // This destructor will run only if the Dispose method 
   // does not get called.
   // It gives your base class the opportunity to finalize.
   // Do not provide destructors in types derived from this class.
   ~BaseResource()      
   {
      // Do not re-create Dispose clean-up code here.
      // Calling Dispose(false) is optimal in terms of
      // readability and maintainability.
      Dispose(false);
   }

   // Allow your Dispose method to be called multiple times,
   // but throw an exception if the object has been disposed.
   // Whenever you do something with this class, 
   // check to see if it has been disposed.
   public void DoSomething()
   {
      if(this.disposed)
      {
         throw new ObjectDisposedException();
      }
   }
}

// Design pattern for a derived class.
// Note that this derived class inherently implements the 
// IDisposable interface because it is implemented in the base class.
public class MyResourceWrapper: BaseResource
{
   // A managed resource that you add in this derived class.
   private ManagedResource addedManaged;
   // A native unmanaged resource that you add in this derived class.
   private NativeResource addedNative;
   private bool disposed = false;

  // Constructor for this object.
   public MyResourceWrapper()
   {
      // Insert appropriate constructor code here.
   }

   protected override void Dispose(bool disposing)
   {
      if(!this.disposed)
      {
         try
         {
            if(disposing)
            {
               // Release the managed resources you added in
               // this derived class here.
               addedManaged.Dispose();         
            }
            // Release the native unmanaged resources you added
            // in this derived class here.
            CloseHandle(addedNative);
            this.disposed = true;
         }
         finally
         {
            // Call Dispose on your base class.
            base.Dispose(disposing);
         }
      }
   }
}

// This derived class does not have a Finalize method
// or a Dispose method without parameters because it inherits 
// them from the base class.

Implementieren einer Close-Methode

Für Typen, die eher eine Close-Methode als eine Dispose-Methode aufrufen, fügen Sie dem Basistyp eine öffentliche Close-Methode hinzu. Die Close-Methode ruft wiederum die Dispose-Methode ohne Parameter auf, die die geeigneten Bereinigungsvorgänge ausführt. Das folgende Codebeispiel veranschaulicht eine Close-Methode.

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

Siehe auch

Referenz

GC.SuppressFinalize Method
Destructors and Finalizers in Visual C++
Implementieren der Methoden "Finalize" und "Dispose" zum Bereinigen von nicht verwalteten Ressourcen

Konzepte

Überschreiben der Finalize-Methode