Freigeben über


Implementieren einer Dispose-Methode

Die Dispose-Methode wird in erster Linie implementiert, um nicht verwaltete Ressourcen freizugeben. Beim Arbeiten mit Instanzmembern, die IDisposable-Implementierungen sind, werden Dispose-Aufrufe häufig weitergegeben. Es gibt andere Gründe für die Implementierung von Dispose, z. B. um zugewiesenen Arbeitsspeicher freizugeben, ein Element zu entfernen, das einer Sammlung hinzugefügt wurde, oder um die Freigabe einer abgerufenen Sperre zu signalisieren.

Der .NET Garbage Collector weist keinen nicht verwalteten Speicher zu oder gibt diesen frei. Das Muster zum Löschen eines Objekts, das als Dispose-Muster bezeichnet wird, legt die Reihenfolge für die Lebensdauer eines Objekts fest. Das Dispose-Muster wird für Objekte verwendet, die die IDisposable Schnittstelle implementieren. Dieses Muster tritt häufig bei der Interaktion mit Datei- und Pipehandles, Registrierungshandles, wait-Handles oder Zeigern auf Blöcke nicht verwalteten Arbeitsspeichers auf, da der Garbage Collector nicht verwaltete Objekte nicht wieder beanspruchen kann.

Damit sichergestellt ist, dass Ressourcen immer ordnungsgemäß bereinigt werden, sollte eine Dispose-Methode idempotent sein, was bedeutet, dass sie mehrmals aufgerufen werden kann, ohne eine Ausnahme auszulösen. Darüber hinaus sollten nachfolgende Aufrufe von Dispose nichts tun.

Das Codebeispiel für die GC.KeepAlive-Methode veranschaulicht, wie durch Garbage Collection die Ausführung eines Finalizers bewirkt werden kann, während ein nicht verwalteter Verweis auf das Objekt oder den Member weiterhin verwendet wird. Es kann sinnvoll sein, GC.KeepAlive zu verwenden, um das Objekt vom Beginn der aktuellen Routine bis zu dem Punkt, an dem diese Methode aufgerufen wird, für die Garbage Collection unzugänglich zu machen.

Tipp

In Bezug auf die Abhängigkeitsinjektion (DI) wird die IServiceCollection beim Registrieren von Diensten in einer implizit in Ihrem Namen verwaltet. Der IServiceProvider und die entsprechende IHost-Orchestrierungsressourcenbereinigung. Insbesondere werden Implementierungen von IDisposable und IAsyncDisposable am Ende ihrer angegebenen Lebensdauer ordnungsgemäß entsorgt.

Weitere Informationen finden Sie unter Abhängigkeitsinjektion in .NET.

Weitergeben von Dispose-Aufrufen

Wenn Ihre Klasse eine Instanz eines anderen Typs besitzt, der IDisposable implementiert, sollte die enthaltende Klasse selbst auch IDisposable implementieren. In der Regel ist eine Klasse, die eine IDisposable Implementierung instanziiert und als Instanzmember (oder Eigenschaft) speichert, auch für die Bereinigung verantwortlich. Dadurch wird sichergestellt, dass die löschbaren Typen, auf die verwiesen wird, die Möglichkeit erhalten, eine Bereinigung deterministisch mit der Dispose-Methode auszuführen. Im folgenden Beispiel ist die Klasse sealed (oder NotInheritable in Visual Basic).

using System;

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

Tipp

  • Wenn Ihre Klasse ein IDisposable Feld oder eine Eigenschaft hat, aber nicht besitzt, muss die Klasse IDisposable nicht implementieren. In der Regel wird auch eine Klasse, die das IDisposable untergeordnete Objekt erstellt und speichert, zum Besitzer, aber in einigen Fällen kann der Besitz auf einen anderen IDisposable Typ übertragen werden.
  • Es gibt Fälle, in denen Sie eine null-Überprüfung in einem Finalizer durchführen möchten (einschließlich der Methode, die Dispose(false) von einem Finalizer aufgerufen wird). Einer der Hauptgründe ist, wenn Sie nicht sicher sind, ob die Instanz vollständig initialisiert wurde (z. B. kann eine Ausnahme in einem Konstruktor ausgelöst werden).

Dispose() und Dispose(bool)

Die IDisposable Schnittstelle erfordert die Implementierung einer einzelnen parameterlosen Methode, Dispose. Außerdem sollte jede nicht versiegelte Klasse über eine Dispose(bool)-Überladungsmethode verfügen.

Methodensignaturen sind:

  • public nicht virtuell (NotOverridable in Visual Basic) (IDisposable.Dispose-Implementierung).
  • protected virtual (Overridable in Visual Basic) Dispose(bool).

Die Dispose()-Methode

Da die public, nicht-virtuelle (NotOverridable in Visual Basic), parameterlose Dispose-Methode aufgerufen wird, wenn sie nicht mehr benötigt wird (von einem Benutzer des Typs), hat sie den Zweck, nicht verwaltete Ressourcen freizugeben, allgemeine Bereinigung durchzuführen und anzugeben, dass der Finalizer, falls vorhanden, nicht ausgeführt werden muss. Das Freigeben des tatsächlichen Arbeitsspeichers, der einem verwalteten Objekt zugeordnet ist, ist immer die Domäne des Garbage Collector. Aus diesem Gründen verfügt sie über eine Standardimplementierung:

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

Die Dispose-Methode führt die Bereinigung aller Objekte aus, damit der Garbage Collector nicht mehr die Object.Finalize-Überschreibung der Objekte aufrufen muss. Daher verhindert der Aufruf der SuppressFinalize-Methode, dass der Garbage Collector den Finalizer ausführt. Wenn der Typ keinen Finalizer besitzt, hat der Aufruf von GC.SuppressFinalize keine Auswirkungen. Die tatsächliche Bereinigung wird durch die Dispose(bool)-Methodenüberladung ausgeführt.

Die Überladung der Dispose(bool)-Methode

Bei der Überladung ist der disposing-Parameter ein Boolean, der angibt, ob der Methodenaufruf von einer Dispose-Methode (sein Wert ist true) oder von einem Finalizer (sein Wert ist false) kommt.

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

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

    // Free unmanaged resources.
    // ...

    _disposed = true;
}
Protected Overridable Sub Dispose(disposing As Boolean)
     If disposed Then Exit Sub

     If disposing Then

         ' Free managed resources.
         ' ...

     End If

     ' Free unmanaged resources.
     ' ...

     disposed = True
End Sub

Wichtig

Der parameter disposing sollte beim Aufruf eines Finalizers false werden, und true, wenn er von der IDisposable.Dispose-Methode aufgerufen wird. Mit anderen Worten, es ist true, wenn es deterministisch aufgerufen wird, und false, wenn es nicht deterministisch aufgerufen wird.

Der Textkörper der Methode besteht aus drei Codeblöcken:

  • Einem Block für die bedingte Rückgabe, wenn das Objekt bereits gelöscht wurde.

  • Ein bedingter Block, der verwaltete Ressourcen freigibt. Dieser Block wird ausgeführt, wenn der Wert von disposingtrueist. Die verwalteten Ressourcen, die sie freigibt, können Folgendes umfassen:

    • Verwaltete Objekte, die IDisposableimplementieren. Der bedingte Block kann verwendet werden, um deren Dispose-Implementierung aufzurufen (Weitergeben von Dispose-Aufrufen). Wenn Sie eine abgeleitete Klasse von System.Runtime.InteropServices.SafeHandle zum Umschließen Ihrer nicht verwalteten Ressource verwendet haben, sollten Sie hier die SafeHandle.Dispose() Implementierung aufrufen.
    • Verwaltete Objekte, die große Speichermengen verbrauchen oder knappe Ressourcen verbrauchen. Weisen Sie großen verwalteten Objektverweisen null zu, damit sie mit größerer Wahrscheinlichkeit nicht erreichbar sind. Dadurch werden sie schneller freigegeben, als wenn sie nicht auf deterministische Weise zurückgewonnen würden.
  • Ein Block, der nicht verwaltete Ressourcen freigibt. Dieser Block wird unabhängig vom Wert des disposing Parameters ausgeführt.

Wenn der Methodenaufruf von einem Finalizer stammt, sollte nur der Code ausgeführt werden, der nicht verwaltete Ressourcen freigibt. Der Implementierer ist dafür verantwortlich, sicherzustellen, dass der falsche Pfad nicht mit verwalteten Objekten interagiert, die möglicherweise verworfen wurden. Dies ist wichtig, da die Reihenfolge, in der der Garbage Collector verwaltete Objekte während der Fertigstellung entfernt, nicht deterministisch ist.

Implementieren des Dispose-Musters

Alle nicht versiegelten Klassen (oder Visual Basic-Klassen, die nicht als NotInheritablegeändert wurden) sollten als potenzielle Basisklasse betrachtet werden, da sie geerbt werden können. Wenn Sie das Dispose-Muster für eine mögliche Basisklasse implementieren, müssen Sie der Klasse die folgenden Methoden hinzufügen:

  • Eine Dispose Implementierung, die die Dispose(bool)-Methode aufruft.
  • Eine Dispose(bool)-Methode, die die eigentliche Bereinigung ausführt.
  • Wenn Ihre Klasse nicht verwaltete Ressourcen behandelt, stellen Sie entweder eine Außerkraftsetzung für die Object.Finalize Methode bereit, oder umschließen Sie die nicht verwaltete Ressource in einer SafeHandle.

Wichtig

Ein Finalizer (eine Object.Finalize Überschreibung) ist nur erforderlich, wenn Sie direkt auf nicht verwaltete Ressourcen verweisen. Dies ist ein sehr fortgeschrittenes Szenario, das in der Regel vermieden werden kann:

  • Wenn Ihre Klasse nur auf verwaltete Objekte verweist, ist es dennoch möglich, dass die Klasse das Dispose-Muster implementieren kann. Es ist nicht erforderlich, einen Finalizer zu implementieren.
  • Wenn Sie mit nicht verwalteten Ressourcen umgehen müssen, legen wir Ihnen dringend ans Herz, das nicht verwaltete IntPtr Handle in ein SafeHandle einzupacken. Das SafeHandle stellt einen Finalizer bereit, sodass Sie keinen selbst schreiben müssen. Weitere Informationen finden Sie im Absatz " Sichere Ziehpunkte ".

Basisklasse mit verwalteten Ressourcen

Hier ist ein allgemeines Beispiel für die Implementierung des Dispose-Musters für eine Basisklasse, die nur verwaltete Ressourcen besitzt.

using System;
using System.IO;

public class DisposableBase : IDisposable
{
    // Detect redundant Dispose() calls.
    private bool _isDisposed;

    // Instantiate a disposable object owned by this class.
    private Stream? _managedResource = new MemoryStream();

    // 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 (!_isDisposed)
        {
            _isDisposed = true;

            if (disposing)
            {
                // Dispose managed state.
                _managedResource?.Dispose();
                _managedResource = null;
            }
        }
    }
}
Imports System.IO

Public Class DisposableBase
    Implements IDisposable

    ' Detect redundant Dispose() calls.
    Private _isDisposed As Boolean

    ' Instantiate a disposable object owned by this class.
    Private _managedResource As Stream = New MemoryStream()

    ' 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 Not _isDisposed Then
            _isDisposed = True

            If disposing Then
                ' Dispose managed state.
                _managedResource?.Dispose()
                _managedResource = Nothing
            End If
        End If
    End Sub
End Class

Anmerkung

Im vorherigen Beispiel wird ein DummyobjektMemoryStream verwendet, um das Muster zu veranschaulichen. Stattdessen kann beliebiger IDisposable verwendet werden.

Basisklasse mit nicht verwalteten Ressourcen

Hier ist ein Beispiel für die Implementierung des Dispose-Musters für eine Basisklasse, die Object.Finalize überschreibt, um die nicht verwalteten Ressourcen, die sie besitzt, zu bereinigen. Das Beispiel veranschaulicht außerdem eine Möglichkeit, Dispose(bool) threadsicher zu implementieren. Die Synchronisierung kann beim Umgang mit nicht verwalteten Ressourcen in einer Multi-Thread-Anwendung entscheidend sein. Wie bereits erwähnt, ist dies ein erweitertes Szenario.

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;

public class DisposableBaseWithFinalizer : IDisposable
{
    // Detect redundant Dispose() calls in a thread-safe manner.
    // _isDisposed == 0 means Dispose(bool) has not been called yet.
    // _isDisposed == 1 means Dispose(bool) has been already called.
    private int _isDisposed;

    // Instantiate a disposable object owned by this class.
    private Stream? _managedResource = new MemoryStream();

    // A pointer to 10 bytes allocated on the unmanaged heap.
    private IntPtr _unmanagedResource = Marshal.AllocHGlobal(10);

    ~DisposableBaseWithFinalizer() => 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)
    {
        // In case _isDisposed is 0, atomically set it to 1.
        // Enter the branch only if the original value is 0.
        if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0)
        {
            if (disposing)
            {
                _managedResource?.Dispose();
                _managedResource = null;
            }

            Marshal.FreeHGlobal(_unmanagedResource);
        }
    }
}
Imports System
Imports System.IO
Imports System.Runtime.InteropServices
Imports System.Threading

Public Class DisposableBaseWithFinalizer
    Implements IDisposable

    ' Detect redundant Dispose() calls in a thread-safe manner.
    ' _isDisposed == 0 means Dispose(bool) has not been called yet.
    ' _isDisposed == 1 means Dispose(bool) has been already called.
    Private _isDisposed As Integer

    ' Instantiate a disposable object owned by this class.
    Private _managedResource As Stream = New MemoryStream()

    ' A pointer to 10 bytes allocated on the unmanaged heap.
    Private _unmanagedResource As IntPtr = Marshal.AllocHGlobal(10)

    Protected Overrides Sub Finalize()
        Dispose(False)
    End Sub

    ' 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)
        ' In case _isDisposed is 0, atomically set it to 1.
        ' Enter the branch only if the original value is 0.
        If Interlocked.CompareExchange(_isDisposed, 1, 0) = 0 Then
            If disposing Then
                _managedResource?.Dispose()
                _managedResource = Nothing
            End If

            Marshal.FreeHGlobal(_unmanagedResource)
        End If
    End Sub
End Class

Anmerkung

Tipp

In C# implementieren Sie eine Finalisierung, indem Sie einen Finalizerbereitstellen, nicht durch Überschreiben von Object.Finalize. In Visual Basic erstellen Sie einen Finalizer mit Protected Overrides Sub Finalize().

Implementieren des Dispose-Musters für eine abgeleitete Klasse

Eine von einer Klasse abgeleitete Klasse, die die IDisposable-Schnittstelle implementiert, sollte nicht IDisposableimplementieren, da die Implementierung der Basisklasse von IDisposable.Dispose von den abgeleiteten Klassen geerbt wird. Um eine abgeleitete Klasse zu bereinigen, geben Sie stattdessen Folgendes an:

  • Eine protected override void Dispose(bool) Methode, die die Basisklassenmethode überschreibt und die tatsächliche Bereinigung der abgeleiteten Klasse durchführt. Diese Methode muss auch die base.Dispose(bool)-Methode (MyBase.Dispose(bool) in Visual Basic) aufrufen und ihr den Disposing-Status (bool disposing-Parameter) als Argument übergeben.
  • Entweder eine von SafeHandle abgeleitete Klasse, die Ihre nicht verwaltete Ressource umschließt (empfohlen), oder eine Überschreibung der Object.Finalize-Methode. Die SafeHandle-Klasse stellt einen Finalizer bereit, der Ihnen erspart, einen zu schreiben. Wenn Sie einen Finalizer bereitstellen, muss er die Dispose(bool)-Überladung mit dem false-Argument aufrufen.

Im Folgenden finden Sie ein Beispiel für das allgemeine Muster zur Implementierung des Dispose-Musters für eine abgeleitete Klasse, die ein sicheres Handle verwendet:

using System.IO;

public class DisposableDerived : DisposableBase
{
    // To detect redundant calls
    private bool _isDisposed;

    // Instantiate a disposable object owned by this class.
    private Stream? _managedResource = new MemoryStream();

    // Protected implementation of Dispose pattern.
    protected override void Dispose(bool disposing)
    {
        if (!_isDisposed)
        {
            _isDisposed = true;

            if (disposing)
            {
                _managedResource?.Dispose();
                _managedResource = null;
            }
        }

        // Call base class implementation.
        base.Dispose(disposing);
    }
}
Imports System.IO

Public Class DisposableDerived
    Inherits DisposableBase

    ' To detect redundant calls
    Private _isDisposed As Boolean

    ' Instantiate a disposable object owned by this class.
    Private _managedResource As Stream = New MemoryStream()

    ' Protected implementation of Dispose pattern.
    Protected Overrides Sub Dispose(disposing As Boolean)
        If Not _isDisposed Then
            _isDisposed = True

            If disposing Then
                _managedResource?.Dispose()
                _managedResource = Nothing
            End If
        End If

        ' Call base class implementation.
        MyBase.Dispose(disposing)
    End Sub
End Class

Anmerkung

Im vorherigen Beispiel wird ein SafeFileHandle-Objekt verwendet, um das Muster zu veranschaulichen; Stattdessen können alle von SafeHandle abgeleiteten Objekte verwendet werden. Beachten Sie, dass das Beispiel sein SafeFileHandle Objekt nicht ordnungsgemäß instanziiert.

Im Folgenden finden Sie das allgemeine Muster für das Implementieren des Dispose-Musters für eine abgeleitete Klasse, die Object.Finalize überschreibt:

using System.Threading;

public class DisposableDerivedWithFinalizer : DisposableBaseWithFinalizer
{
    // Detect redundant Dispose() calls in a thread-safe manner.
    // _isDisposed == 0 means Dispose(bool) has not been called yet.
    // _isDisposed == 1 means Dispose(bool) has been already called.
    private int _isDisposed;

    ~DisposableDerivedWithFinalizer() => Dispose(false);

    // Protected implementation of Dispose pattern.
    protected override void Dispose(bool disposing)
    {
        // In case _isDisposed is 0, atomically set it to 1.
        // Enter the branch only if the original value is 0.
        if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0)
        {
            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.
        }

        // Call the base class implementation.
        base.Dispose(disposing);
    }
}
Imports System.Threading

Public Class DisposableDerivedWithFinalizer
    Inherits DisposableBaseWithFinalizer

    ' Detect redundant Dispose() calls in a thread-safe manner.
    ' _isDisposed == 0 means Dispose(bool) has not been called yet.
    ' _isDisposed == 1 means Dispose(bool) has been already called.
    Private _isDisposed As Integer

    Protected Overrides Sub Finalize()
        Dispose(False)
    End Sub

    ' Protected implementation of Dispose pattern.
    Protected Overrides Sub Dispose(disposing As Boolean)
        ' In case _isDisposed is 0, atomically set it to 1.
        ' Enter the branch only if the original value is 0.
        If Interlocked.CompareExchange(_isDisposed, 1, 0) = 0 Then
            If disposing Then
                ' TODO: dispose managed state (managed objects).
            End If

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

        ' Call the base class implementation.
        MyBase.Dispose(disposing)
    End Sub
End Class

Sichere Griffe

Das Schreiben von Code für den Finalizer eines Objekts ist eine komplexe Aufgabe, die Probleme verursachen kann, wenn sie nicht ordnungsgemäß ausgeführt werden. Daher wird empfohlen, System.Runtime.InteropServices.SafeHandle Objekte zu erstellen, anstatt einen Finalizer zu implementieren.

Ein System.Runtime.InteropServices.SafeHandle ist ein abstrakter verwalteter Typ, der eine System.IntPtr umschließt, die eine nicht verwaltete Ressource identifiziert. Unter Windows könnte es ein Handle bezeichnen, und unter UNIX einen Dateideskriptor. Die SafeHandle stellt alle Logik bereit, die erforderlich ist, um sicherzustellen, dass diese Ressource genau einmal freigegeben wird, entweder wenn der SafeHandle entsorgt wird oder wenn alle Verweise auf die SafeHandle gelöscht wurden und die SafeHandle Instanz finalisiert ist.

Die System.Runtime.InteropServices.SafeHandle ist eine abstrakte Basisklasse. Abgeleitete Klassen stellen bestimmte Instanzen für verschiedene Arten von Handles bereit. Diese abgeleiteten Klassen überprüfen, welche Werte für die System.IntPtr als ungültig betrachtet werden und wie sie das Handle tatsächlich freigeben. Beispielsweise wird SafeFileHandle von SafeHandle abgeleitet, um IntPtrs einzuschließen, die geöffnete Dateihandles und -deskriptoren identifizieren, und überschreibt seine SafeHandle.ReleaseHandle()-Methode zum Schließen (über die close Funktion unter Unix oder die CloseHandle Funktion unter Windows). Die meisten APIs in .NET-Bibliotheken, die eine nicht verwaltete Ressource erstellen, kapseln sie in einem SafeHandle und geben diesen SafeHandle bei Bedarf an Sie zurück, anstatt den rohen Zeiger zurückzugeben. In Situationen, in denen Sie mit einer nicht verwalteten Komponente interagieren und eine IntPtr für eine nicht verwaltete Ressource abrufen, können Sie einen eigenen Typ SafeHandle erstellen, um sie einzuschließen. Daher müssen nur wenige Nicht-SafeHandle Typen Finalizer implementieren. Die meisten Implementierungen des Disposable-Patterns enden nur damit, andere verwaltete Ressourcen zu ummanteln, von denen einige SafeHandle Objekte sein können.

Implementieren des Dispose-Musters mithilfe eines benutzerdefinierten sicheren Handles

Der folgende Code veranschaulicht, wie nicht verwaltete Ressourcen durch Implementierung von SafeHandle gehandhabt werden.

using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

// Wraps the IntPtr allocated by Marshal.AllocHGlobal() into a SafeHandle.
class LocalAllocHandle : SafeHandleZeroOrMinusOneIsInvalid
{
    private LocalAllocHandle() : base(ownsHandle: true) { }

    // No need to implement a finalizer - SafeHandle's finalizer will call ReleaseHandle for you.
    protected override bool ReleaseHandle()
    {
        Marshal.FreeHGlobal(handle);
        return true;
    }

    // Allocate bytes with Marshal.AllocHGlobal() and wrap the result into a SafeHandle.
    public static LocalAllocHandle Allocate(int numberOfBytes)
    {
        IntPtr nativeHandle = Marshal.AllocHGlobal(numberOfBytes);
        LocalAllocHandle safeHandle = new LocalAllocHandle();
        safeHandle.SetHandle(nativeHandle);
        return safeHandle;
    }
}

public class DisposableBaseWithSafeHandle : IDisposable
{
    // Detect redundant Dispose() calls.
    private bool _isDisposed;

    // Managed disposable objects owned by this class
    private LocalAllocHandle? _safeHandle = LocalAllocHandle.Allocate(10);
    private Stream? _otherUnmanagedResource = new MemoryStream();

    // 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 (!_isDisposed)
        {
            _isDisposed = true;

            if (disposing)
            {
                // Dispose managed state.
                _otherUnmanagedResource?.Dispose();
                _safeHandle?.Dispose();
                _otherUnmanagedResource = null;
                _safeHandle = null;
            }
        }
    }
}
Imports System
Imports System.IO
Imports System.Runtime.InteropServices
Imports Microsoft.Win32.SafeHandles

' Wraps the IntPtr allocated by Marshal.AllocHGlobal() into a SafeHandle.
Public Class LocalAllocHandle
    Inherits SafeHandleZeroOrMinusOneIsInvalid

    Private Sub New()
        MyBase.New(True)
    End Sub

    ' No need to implement a finalizer - SafeHandle's finalizer will call ReleaseHandle for you.
    Protected Overrides Function ReleaseHandle() As Boolean
        Marshal.FreeHGlobal(handle)
        Return True
    End Function

    ' Allocate bytes with Marshal.AllocHGlobal() and wrap the result into a SafeHandle.
    Public Shared Function Allocate(numberOfBytes As Integer) As LocalAllocHandle
        Dim nativeHandle As IntPtr = Marshal.AllocHGlobal(numberOfBytes)
        Dim safeHandle As New LocalAllocHandle()
        safeHandle.SetHandle(nativeHandle)
        Return safeHandle
    End Function
End Class

Public Class DisposableBaseWithSafeHandle
    Implements IDisposable

    ' Detect redundant Dispose() calls.
    Private _isDisposed As Boolean

    ' Managed disposable objects owned by this class
    Private _safeHandle As LocalAllocHandle = LocalAllocHandle.Allocate(10)
    Private _otherUnmanagedResource As Stream = New MemoryStream()

    ' 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 Not _isDisposed Then
            _isDisposed = True

            If disposing Then
                ' Dispose managed state.
                _otherUnmanagedResource?.Dispose()
                _safeHandle?.Dispose()
                _otherUnmanagedResource = Nothing
                _safeHandle = Nothing
            End If
        End If
    End Sub
End Class

Anmerkung

Das Verhalten der DisposableBaseWithSafeHandle Klasse entspricht dem Verhalten der DisposableBaseWithFinalizer Klasse in einem vorherigen Beispiel. Der hier gezeigte Ansatz ist jedoch sicherer:

  • Es ist nicht erforderlich, einen Finalizer zu implementieren, da SafeHandle die Finalisierung übernimmt.
  • Es ist keine Synchronisierung erforderlich, um die Threadsicherheit zu gewährleisten. Obwohl es bei der Dispose-Implementierung von DisposableBaseWithSafeHandle eine Rennbedingung gibt, garantiert SafeHandle, dass SafeHandle.ReleaseHandle nur einmal aufgerufen wird.

Integrierte sichere Griffe in .NET

Die folgenden abgeleiteten Klassen im Microsoft.Win32.SafeHandles-Namespace stellen sichere Handles bereit.

Klasse Ressourcen, die sie enthält
SafeFileHandle
SafeMemoryMappedFileHandle
SafePipeHandle
Dateien, im Arbeitsspeicher abgebildete Dateien und Pipes
SafeMemoryMappedViewHandle Speicheransichten
SafeNCryptKeyHandle
SafeNCryptProviderHandle
SafeNCryptSecretHandle
Kryptografiekonstrukte
SafeRegistryHandle Registrierungsschlüssel
SafeWaitHandle Wait-Handles

Siehe auch