Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
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, dieDispose(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:
-
publicnicht virtuell (NotOverridablein Visual Basic) (IDisposable.Dispose-Implementierung). -
protected virtual(Overridablein 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
nullzu, 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
disposingParameters 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
- Im vorherigen Beispiel werden AllocHGlobal 10 Bytes für den nicht verwalteten Heap im Konstruktor zugewiesen und der Puffer in
Dispose(bool)durch Aufrufen von FreeHGlobal freigegeben. Dies ist eine Dummy-Zuordnung zu Veranschaulichungszwecken. - Auch hier wird empfohlen, die Implementierung eines Finalizers zu vermeiden. Siehe Implementieren des Dispose-Musters mit einem benutzerdefinierten sicheren Handle für ein Äquivalent des vorherigen Beispiels, bei dem die nicht-deterministische Finalisierung und Synchronisierung an SafeHandle delegiert werden.
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 diebase.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 demfalse-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 vonDisposableBaseWithSafeHandleeine 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 |