Delen via


Een verwijderingsmethode implementeren

De methode Dispose wordt voornamelijk geïmplementeerd om onbeheerde resources vrij te geven. Wanneer u werkt met exemplaarleden die IDisposable-implementaties zijn, is het gebruikelijk om Dispose-aanroepen trapsgewijs uit te voeren. Er zijn andere redenen om Disposete implementeren, bijvoorbeeld om geheugen vrij te maken dat is toegewezen, een item te verwijderen dat aan een verzameling is toegevoegd of om de release van een vergrendeling te signaleren die is verkregen.

De .NET garbage collector wijst geen onbeheerd geheugen toe of laat het los. Het patroon voor het verwijderen van een object, aangeduid als het verwijderingspatroon, legt orde op de levensduur van een object. Het dispose-patroon wordt gebruikt voor objecten die de IDisposable-interface implementeren. Dit patroon is gebruikelijk bij interactie met bestands- en pijpgrepen, registergrepen, wachtgrepen of aanwijzers naar blokken onbeheerd geheugen, omdat de garbagecollector niet in staat is onbeheerde objecten vrij te maken.

Om ervoor te zorgen dat resources altijd correct worden opgeschoond, moet een Dispose methode idempotent zijn, zodat deze meerdere keren kan worden aangeroepen zonder een uitzondering te genereren. Verder mogen volgende aanroepen van Dispose niets doen.

In het codevoorbeeld voor de GC.KeepAlive methode ziet u hoe garbagecollection kan leiden tot uitvoering van een finalizer terwijl een niet-beheerde verwijzing naar het object of de leden ervan nog in gebruik is. Het kan zinvol zijn om GC.KeepAlive te gebruiken om het object uit te sluiten van de garbage-collectie vanaf het begin van de huidige routine, tot het punt waar deze methode wordt aangeroepen.

Tip

Wat afhankelijkheidsinjectie betreft, wordt bij het registreren van services in een IServiceCollectionde levensduur van de service impliciet namens u beheerd. De IServiceProvider en de bijbehorende IHost coördineren het opschonen van middelen. Met name worden implementaties van IDisposable en IAsyncDisposable aan het einde van hun opgegeven levensduur correct verwijderd.

Zie Afhankelijkheidsinjectie in .NETvoor meer informatie.

Oproepen cascaderend afhandelen

Als uw klasse eigenaar is van een exemplaar van een ander type dat IDisposableimplementeert, moet de betreffende klasse zelf ook IDisposableimplementeren. Doorgaans is een klasse die een instantie van een IDisposable-implementatie maakt en opslaat als instantie-element (of -eigenschap) tevens verantwoordelijk voor het opschonen ervan. Dit helpt ervoor te zorgen dat de genoemde wegwerptypen de kans krijgen om via de Dispose-methode deterministisch op te ruimen. In het volgende voorbeeld is de klasse sealed (of 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

Tip

  • Als uw klasse een IDisposable veld of eigenschap heeft, maar het niet bezit, hoeft de klasse geen IDisposablete implementeren. Meestal wordt een klasse die het IDisposable kindobject maakt en opslaat ook de eigenaar, maar in sommige gevallen kan het eigendom worden overgedragen op een ander IDisposable type.
  • Er zijn gevallen waarin u een null-controle in een finalizer wilt uitvoeren (inclusief de Dispose(false)-methode die wordt opgeroepen door een finalizer). Een van de belangrijkste redenen is dat u niet zeker weet of de instantie volledig is geïnitialiseerd (er kan bijvoorbeeld een uitzondering in een constructor worden geworpen).

Dispose() and Dispose(bool)

De IDisposable-interface vereist de implementatie van één methode zonder parameters, Dispose. Bovendien moet elke niet-verzegelde klasse een Dispose(bool) overloadmethode hebben.

Methodesignaturen zijn:

  • public niet-virtueel (NotOverridable in Visual Basic) (IDisposable.Dispose implementatie).
  • `protected virtual (Overridable in Visual Basic) Dispose(bool).`

De methode Dispose()

Omdat de public, niet-virtueel (NotOverridable in Visual Basic), parameterloze Dispose methode wordt aangeroepen wanneer deze niet meer nodig is (door een consument van het type), is het doel ervan om onbeheerde resources vrij te maken, algemene opschoning uit te voeren en aan te geven dat de finalizer, indien aanwezig, niet hoeft te worden uitgevoerd. Het vrijmaken van het werkelijke-geheugen dat is gekoppeld aan een beheerd-object is altijd het domein van de garbagecollector. Daarom heeft het een standaard implementatie:

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

De Dispose-methode voert alle opruimwerkzaamheden voor objecten uit, zodat de garbagecollector de overschrijving van de objecten' Object.Finalize niet meer hoeft aan te roepen. Daarom voorkomt de aanroep van de SuppressFinalize methode dat de garbagecollector de finalizer uitvoert. Als het type geen finalizer heeft, heeft de aanroep naar GC.SuppressFinalize geen effect. De werkelijke opschoning wordt uitgevoerd door de Dispose(bool) methode-overload.

De overbelasting van de methode Dispose(bool)

In de overbelasting is de parameter disposing een Boolean die aangeeft of de methode-aanroep afkomstig is van een Dispose methode (de waarde is true) of van een finalizer (de waarde is false).

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

Belangrijk

De parameter disposing moet worden false wanneer deze wordt aangeroepen vanuit een finalizer en true wanneer deze wordt aangeroepen vanuit de methode IDisposable.Dispose. Met andere woorden, het is true wanneer deterministisch aangeroepen en false wanneer niet-deterministisch wordt aangeroepen.

De hoofdtekst van de methode bestaat uit drie codeblokken:

  • Een blok voor voorwaardelijk retourneren als het object al is verwijderd.

  • Een voorwaardelijk blok waarmee beheerde resources worden vrijgemaakt. Dit blok wordt uitgevoerd als de waarde van disposing is true. De beheerde resources die worden vrijgemaakt, kunnen het volgende omvatten:

    • Beheerde objecten die IDisposableimplementeren. Het voorwaardelijke blok kan worden gebruikt om hun Dispose implementatie aan te roepen (trapsgewijs verwijderen). Als u een afgeleide klasse van System.Runtime.InteropServices.SafeHandle hebt gebruikt om uw onbeheerde resource te verpakken, moet u de SafeHandle.Dispose() implementatie hier aanroepen.
    • Beheerde objecten die grote hoeveelheden geheugen verbruiken of schaarse resources verbruiken. Wijs grote beheerde objectverwijzingen toe aan null om ze waarschijnlijk onbereikbaar te maken. Dit brengt ze sneller vrij dan als ze niet-deterministisch werden vrijgemaakt.
  • Een blok waarmee onbeheerde resources worden vrijgemaakt. Dit blok wordt uitgevoerd, ongeacht de waarde van de parameter disposing.

Als de methode-aanroep afkomstig is van een finalizer, moet alleen de code worden uitgevoerd die onbeheerde resources vrijgeeft. De implementator is verantwoordelijk voor het verzekeren dat het vals pad niet omgaat met beheerde objecten die mogelijk zijn verwijderd. Dit is belangrijk omdat de volgorde waarin de garbagecollector beheerde objecten verwijdert tijdens het voltooien niet-deterministisch is.

Het verwijderingspatroon implementeren

Alle niet-verzegelde klassen (of Visual Basic-klassen die niet zijn gewijzigd als NotInheritable) moeten worden beschouwd als een mogelijke basisklasse, omdat ze kunnen worden overgenomen. Als u het verwijderingspatroon voor een mogelijke basisklasse implementeert, moet u de volgende methoden toevoegen aan uw klasse:

  • Een Dispose-implementatie die de methode Dispose(bool) aanroept.
  • Een Dispose(bool) methode waarmee de werkelijke opschoning wordt uitgevoerd.
  • Als uw klasse omgaat met onbeheerde bronnen, geeft u een overschrijving op voor de methode Object.Finalize of verpakt u de onbeheerde bron in een SafeHandle.

Belangrijk

Een finalizer (een Object.Finalize-overschrijving) is alleen vereist als u rechtstreeks verwijst naar niet-beheerde resources. Dit is een zeer geavanceerd scenario dat doorgaans kan worden vermeden:

  • Als uw klasse verwijst naar alleen beheerde objecten, is het nog steeds mogelijk voor de klasse om het verwijderingspatroon te implementeren. U hoeft geen finalizer te implementeren.
  • Als u niet-beheerde resources moet verwerken, raden we u ten zeerste aan de onbeheerde IntPtr handle in een SafeHandlete verpakken. De SafeHandle biedt een finalizer, zodat u er zelf geen hoeft te schrijven. Zie de Veilige handgrepen alinea voor meer informatie.

Basisklasse met beheerde resources

Hier volgt een algemeen voorbeeld van het implementeren van het verwijderingspatroon voor een basisklasse die alleen eigenaar is van beheerde resources.

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

Notitie

In het vorige voorbeeld wordt een dummy-MemoryStream-object gebruikt om het patroon te illustreren. Elke IDisposable kan in plaats daarvan worden gebruikt.

Basisklasse met niet-beheerde resources

Hier volgt een voorbeeld voor het implementeren van het verwijderingspatroon voor een basisklasse die Object.Finalize overschrijft om onbeheerde resources op te schonen die eigendom zijn van deze klasse. In het voorbeeld ziet u ook een manier om Dispose(bool) op een thread-veilige manier te implementeren. Synchronisatie kan essentieel zijn bij het omgaan met niet-beheerde resources in een toepassing met meerdere threads. Zoals eerder vermeld, is dit een geavanceerd scenario.

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

Notitie

Tip

In C# implementeert u een finalisatie door een finalizer op te geven, niet door een override van Object.Finalizeuit te voeren. In Visual Basic maakt u een finalizer met Protected Overrides Sub Finalize().

Het verwijderingspatroon voor een afgeleide klasse implementeren

Een klasse die is afgeleid van een klasse die de IDisposable-interface implementeert, mag geen IDisposableimplementeren, omdat de basisklasse-implementatie van IDisposable.Dispose wordt overgenomen door de afgeleide klassen. Als u een afgeleide klasse wilt opschonen, geeft u het volgende op:

  • Een protected override void Dispose(bool) methode die de basisklassemethode overschrijft en de werkelijke opschoonactie van de afgeleide klasse uitvoert. Met deze methode moet ook de methode base.Dispose(bool) (MyBase.Dispose(bool) in Visual Basic) worden aangeroepen die de disponerende status (bool disposing parameter) doorgeeft als argument.
  • Een klasse die is afgeleid van SafeHandle die uw onbeheerde resource verpakt (aanbevolen) of een overschrijving naar de Object.Finalize methode. De SafeHandle-klasse biedt een finalizer waarmee u geen code hoeft uit te voeren. Als u een finalizer opgeeft, moet deze de Dispose(bool) overload-functie aanroepen met argument false.

Hier volgt een voorbeeld van het algemene patroon voor het implementeren van het verwijderingspatroon voor een afgeleide klasse die gebruikmaakt van een veilige ingang:

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

Notitie

In het vorige voorbeeld wordt een SafeFileHandle object gebruikt om het patroon te illustreren; elk object dat is afgeleid van SafeHandle kan in plaats daarvan worden gebruikt. Let op dat in het voorbeeld het SafeFileHandle-object niet correct wordt geïnitialiseerd.

Dit is het algemene patroon voor het implementeren van het verwijderingspatroon voor een afgeleide klasse die Object.Finalizeoverschrijft:

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

Veilige ingangen

Het schrijven van code voor de finalizer van een object is een complexe taak die problemen kan veroorzaken als deze niet correct worden uitgevoerd. Daarom raden we u aan System.Runtime.InteropServices.SafeHandle objecten te maken in plaats van een finalizer te implementeren.

Een System.Runtime.InteropServices.SafeHandle is een abstract beheerd type dat een System.IntPtr verpakt die een onbeheerde resource identificeert. In Windows kan het een ingang identificeren en op Unix, een bestandsdescriptor. De SafeHandle biedt alle logica die nodig is om ervoor te zorgen dat deze resource eenmaal en slechts één keer wordt vrijgegeven, hetzij wanneer de SafeHandle wordt verwijderd of wanneer alle verwijzingen naar de SafeHandle zijn verwijderd en het SafeHandle exemplaar is voltooid.

De System.Runtime.InteropServices.SafeHandle is een abstracte basisklasse. Afgeleide klassen bieden specifieke exemplaren voor verschillende soorten handgrepen. Deze afgeleide klassen valideren welke waarden voor de System.IntPtr als ongeldig worden beschouwd en hoe de handle daadwerkelijk vrijgemaakt wordt. SafeFileHandle is bijvoorbeeld afgeleid van SafeHandle om IntPtrs te verpakken die open bestandskoppelingen en descriptors identificeren, en overschrijft zijn SafeHandle.ReleaseHandle()-methode om deze te sluiten (via de close-functie op Unix of de CloseHandle-functie op Windows). De meeste API's in .NET-bibliotheken die een onbeheerde resource maken, verpakken deze in een SafeHandle en geven SafeHandle indien nodig terug, in plaats van de ruwe pointer terug te geven. In situaties waarin u communiceert met een niet-beheerd onderdeel en een IntPtr voor een onbeheerde resource ontvangt, kunt u uw eigen SafeHandle type maken om het te verpakken. Als gevolg hiervan hoeven weinig niet-SafeHandle typen finalizers te implementeren. De meeste implementaties van wegwerppatronen komen uiteindelijk neer op het omsluiten van andere beheerde resources, waarvan sommige mogelijk SafeHandle-objecten zijn.

Het verwijderingspatroon implementeren door gebruik te maken van een aangepaste safe handle.

De volgende code laat zien hoe u niet-beheerde resources kunt verwerken door een SafeHandlete implementeren.

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

Notitie

Het gedrag van de DisposableBaseWithSafeHandle-klasse is gelijk aan het gedrag van de DisposableBaseWithFinalizer klasse in een eerder voorbeeld, maar de hier getoonde benadering is veiliger:

  • Het is niet nodig om een finalizer te implementeren, omdat SafeHandle zorgt voor de voltooien.
  • Synchronisatie is niet nodig om threadveiligheid te garanderen. Hoewel er een racevoorwaarde is in de Dispose implementatie van DisposableBaseWithSafeHandle, garandeert SafeHandle dat SafeHandle.ReleaseHandle slechts eenmaal wordt aangeroepen.

Ingebouwde veilige ingangen in .NET

De volgende afgeleide klassen in de Microsoft.Win32.SafeHandles naamruimte bieden veilige ingangen.

Klas Resources die het bezit
SafeFileHandle
SafeMemoryMappedFileHandle
SafePipeHandle
Bestanden, geheugengekoppelde bestanden en pijpleidingen
SafeMemoryMappedViewHandle Geheugenweergaven
SafeNCryptKeyHandle
SafeNCryptProviderHandle
SafeNCryptSecretHandle
Cryptografieconstructies
SafeRegistryHandle Registersleutels
SafeWaitHandle Wachtgrepen

Zie ook