Share via


Een verwijderingsmethode implementeren

De Dispose methode wordt voornamelijk geïmplementeerd om onbeheerde resources vrij te geven. Wanneer u werkt met exemplaarleden die implementaties zijn IDisposable , is het gebruikelijk om trapsgewijs aanroepen uit te voeren Dispose . Er zijn andere redenen om bijvoorbeeld geheugen vrij te maken Disposedat 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 garbagecollection wijst geen onbeheerd geheugen toe of laat het los. Het patroon voor het verwijderen van een object, ook wel het verwijderingspatroon genoemd, legt orde op voor de levensduur van een object. Het verwijderingspatroon 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 niets Dispose doen.

In het codevoorbeeld voor de GC.KeepAlive methode ziet u hoe garbagecollection ertoe kan leiden dat een finalizer wordt uitgevoerd terwijl een niet-beheerde verwijzing naar het object of de leden ervan nog in gebruik zijn. Het kan zinvol zijn om het object te gebruiken GC.KeepAlive dat niet in aanmerking komt voor garbagecollection vanaf het begin van de huidige routine tot het punt waar deze methode wordt aangeroepen.

Tip

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

Zie Afhankelijkheidsinjectie in .NET voor meer informatie.

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 objecten samen te stellen System.Runtime.InteropServices.SafeHandle in plaats van een finalizer te implementeren.

A System.Runtime.InteropServices.SafeHandle is een abstract beheerd type waarmee een System.IntPtr onbeheerde resource wordt geïdentificeerd. 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 verwijdering wordt uitgevoerd of wanneer alle verwijzingen naar de SafeHandle resource zijn verwijderd en het SafeHandle exemplaar is voltooid.

Dit System.Runtime.InteropServices.SafeHandle is een abstracte basisklasse. Afgeleide klassen bieden specifieke exemplaren voor verschillende soorten ingangen. Deze afgeleide klassen valideren welke waarden voor de System.IntPtr klassen als ongeldig worden beschouwd en hoe u de ingang daadwerkelijk vrij kunt maken. SafeFileHandle Bijvoorbeeld afgeleid van SafeHandle om te verpakken IntPtrs waarmee geopende bestandsingangen/descriptors worden geïdentificeerd en de methode wordt overschreven SafeHandle.ReleaseHandle() om deze te sluiten (via de close functie op Unix of CloseHandle functie in Windows). De meeste API's in .NET-bibliotheken die een onbeheerde resource maken, verpakt deze in een SafeHandle en retourneren deze SafeHandle indien nodig, in plaats van de onbewerkte aanwijzer terug te geven. In situaties waarin u interactie hebt met een niet-beheerd onderdeel en een IntPtr voor een onbeheerde resource opkrijgt, kunt u uw eigen SafeHandle type maken om het in te pakken. Als gevolg hiervan moeten enkele niet-typenSafeHandle finalizers implementeren. De meeste wegwerppatroon-implementaties verpakken alleen andere beheerde resources, waarvan sommige objecten kunnen zijn SafeHandle .

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

Klas Resources die worden opgeslagen
SafeFileHandle
SafeMemoryMappedFileHandle
SafePipeHandle
Bestanden, geheugen toegewezen bestanden en pijpen
SafeMemoryMappedViewHandle Geheugenweergaven
SafeNCryptKeyHandle
SafeNCryptProviderHandle
SafeNCryptSecretHandle
Cryptografieconstructies
SafeRegistryHandle Registersleutels
SafeWaitHandle Wachtgrepen

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) overbelastingsmethode hebben.

Methodehandtekeningen zijn:

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

De methode Dispose()

Omdat de publicmethode , 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 objectopruiming uit, zodat de garbagecollector de onderdrukking 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 geen GC.SuppressFinalize effect. De werkelijke opschoning wordt uitgevoerd door de overbelasting van de Dispose(bool) methode.

De overbelasting van de methode Dispose(bool)

In de overbelasting is de disposing parameter 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)
    {
        // TODO: dispose managed state (managed objects).
    }

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

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

     ' A block that frees unmanaged resources.
     
     If disposing Then
         ' Deterministic call…
         ' A conditional block that frees managed resources.    	
     End If
     
     disposed = True
End Sub

Belangrijk

De disposing parameter moet zijn wanneer deze wordt false aangeroepen vanuit een finalizer en true wanneer deze wordt aangeroepen vanuit de IDisposable.Dispose methode. 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 blok waarmee onbeheerde resources worden vrijgemaakt. Dit blok wordt uitgevoerd, ongeacht de waarde van de disposing parameter.

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

    • Beheerde objecten die worden geïmplementeerd IDisposable. Het voorwaardelijke blok kan worden gebruikt om de Dispose implementatie aan te roepen (trapsgewijs verwijderen). Als u een afgeleide klasse 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 om null ze waarschijnlijk onbereikbaar te maken. Dit brengt ze sneller vrij dan als ze niet-deterministisch werden vrijgemaakt.

Als de methode-aanroep afkomstig is van een finalizer, moet alleen de code worden uitgevoerd die onbeheerde resources vrijgeeft. De implementeerfunctie is verantwoordelijk voor het controleren of het onwaar-pad niet communiceert 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.

Oproepen trapsgewijs verwijderen

Als uw klasse eigenaar is van een veld of eigenschap en het bijbehorende type implementeert IDisposable, moet de betreffende klasse zelf ook worden geïmplementeerd IDisposable. Een klasse die een IDisposable implementatie instantieert en opslaat als exemplaarlid, is ook verantwoordelijk voor het opschonen ervan. Dit helpt ervoor te zorgen dat de naar verwezen wegwerptypen de mogelijkheid krijgen om deterministisch opschoning uit te voeren via de Dispose methode. In het volgende voorbeeld is sealed de klasse (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 deze niet bezit , wat betekent dat de klasse het object niet maakt, hoeft de klasse het niet te implementeren IDisposable.
  • Er zijn gevallen waarin u het controleren van een finalizer wilt uitvoeren null(inclusief de Dispose(false) methode die wordt aangeroepen door een finalizer). Een van de belangrijkste redenen is dat u niet zeker weet of het exemplaar volledig is geïnitialiseerd (er kan bijvoorbeeld een uitzondering in een constructor worden gegenereerd).

Het verwijderingspatroon implementeren

Alle niet-verzegelde klassen (of Visual Basic-klassen die niet zijn gewijzigd als NotInheritable) moeten worden beschouwd als een potentiële basisklasse, omdat ze kunnen worden overgenomen. Als u het verwijderingspatroon implementeert voor een mogelijke basisklasse, moet u het volgende opgeven:

  • Een Dispose implementatie die de Dispose(bool) methode aanroept.
  • Een Dispose(bool) methode waarmee de werkelijke opschoning wordt uitgevoerd.
  • Een klasse die is afgeleid van SafeHandle deze klasse verpakt uw onbeheerde resource (aanbevolen) of een onderdrukking naar de Object.Finalize methode. De SafeHandle klas biedt een finalizer, dus u hoeft er zelf geen te schrijven.

Belangrijk

Het is mogelijk dat een basisklasse alleen verwijst naar beheerde objecten en het verwijderingspatroon implementeert. In deze gevallen is een finalizer niet nodig. Een finalizer is alleen vereist als u rechtstreeks naar onbeheerde resources verwijst.

Hier volgt een algemeen voorbeeld van het implementeren van het verwijderingspatroon voor een basisklasse die gebruikmaakt van een veilige ingang.

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

public class BaseClassWithSafeHandle : IDisposable
{
    // To detect redundant calls
    private bool _disposedValue;

    // Instantiate a SafeHandle instance.
    private SafeHandle? _safeHandle = new SafeFileHandle(IntPtr.Zero, true);

    // 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 (!_disposedValue)
        {
            if (disposing)
            {
                _safeHandle?.Dispose();
                _safeHandle = null;
            }

            _disposedValue = true;
        }
    }
}
Imports Microsoft.Win32.SafeHandles
Imports System.Runtime.InteropServices

Public Class BaseClassWithSafeHandle
    Implements IDisposable

    ' To detect redundant calls
    Private _disposedValue As Boolean

    ' Instantiate a SafeHandle instance.
    Private _safeHandle As SafeHandle = New SafeFileHandle(IntPtr.Zero, True)

    ' 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(ByVal disposing As Boolean)
        If Not _disposedValue Then

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

            _disposedValue = True
        End If
    End Sub
End Class

Notitie

In het vorige voorbeeld wordt een SafeFileHandle object gebruikt om het patroon te illustreren. In plaats daarvan kan elk object worden gebruikt dat is afgeleid van SafeHandle . Houd er rekening mee dat het voorbeeld het object niet goed instantiëren SafeFileHandle .

Hier volgt het algemene patroon voor het implementeren van het verwijderingspatroon Object.Finalizevoor een basisklasse die overschrijft.

using System;

public class BaseClassWithFinalizer : IDisposable
{
    // To detect redundant calls
    private bool _disposedValue;

    ~BaseClassWithFinalizer() => 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)
    {
        if (!_disposedValue)
        {
            if (disposing)
            {
                // TODO: dispose managed state (managed objects)
            }

            // TODO: free unmanaged resources (unmanaged objects) and override finalizer
            // TODO: set large fields to null
            _disposedValue = true;
        }
    }
}
Public Class BaseClassWithFinalizer
    Implements IDisposable

    ' To detect redundant calls
    Private _disposedValue As Boolean

    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(ByVal disposing As Boolean)
        If Not _disposedValue Then

            If disposing Then
                ' TODO: dispose managed state (managed objects)
            End If

            ' TODO free unmanaged resources (unmanaged objects) And override finalizer
            ' TODO: set large fields to null
            _disposedValue = True
        End If
    End Sub
End Class

Tip

In C# implementeert u een voltooien door een finalizer te bieden, niet door overschrijven Object.Finalize. 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 niet worden geïmplementeerd IDisposable, omdat de basisklasse-implementatie 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 opschoning van de afgeleide klasse uitvoert. Deze methode moet ook de base.Dispose(bool) methode (MyBase.Dispose(bool) in Visual Basic) aanroepen die de status (bool disposing parameter) doorgeeft als argument.
  • Een klasse die is afgeleid van SafeHandle deze klasse verpakt uw onbeheerde resource (aanbevolen) of een onderdrukking naar de Object.Finalize methode. De SafeHandle klasse biedt een finalizer waarmee u geen code hoeft te coden. Als u wel een finalizer opgeeft, moet deze de Dispose(bool) overbelasting aanroepen met false argument.

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 Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;

public class DerivedClassWithSafeHandle : BaseClassWithSafeHandle
{
    // To detect redundant calls
    private bool _disposedValue;

    // Instantiate a SafeHandle instance.
    private SafeHandle? _safeHandle = new SafeFileHandle(IntPtr.Zero, true);

    // Protected implementation of Dispose pattern.
    protected override void Dispose(bool disposing)
    {
        if (!_disposedValue)
        {
            if (disposing)
            {
                _safeHandle?.Dispose();
                _safeHandle = null;
            }

            _disposedValue = true;
        }

        // Call base class implementation.
        base.Dispose(disposing);
    }
}
Imports Microsoft.Win32.SafeHandles
Imports System.Runtime.InteropServices

Public Class DerivedClassWithSafeHandle
    Inherits BaseClassWithSafeHandle

    ' To detect redundant calls
    Private _disposedValue As Boolean

    ' Instantiate a SafeHandle instance.
    Private _safeHandle As SafeHandle = New SafeFileHandle(IntPtr.Zero, True)

    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        If Not _disposedValue Then

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

            _disposedValue = True
        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. In plaats daarvan kan elk object worden gebruikt dat is afgeleid van SafeHandle . Houd er rekening mee dat het voorbeeld het object niet goed instantiëren SafeFileHandle .

Hier volgt het algemene patroon voor het implementeren van het verwijderingspatroon Object.Finalizevoor een afgeleide klasse die overschrijft:

public class DerivedClassWithFinalizer : BaseClassWithFinalizer
{
    // To detect redundant calls
    private bool _disposedValue;

    ~DerivedClassWithFinalizer() => Dispose(false);

    // Protected implementation of Dispose pattern.
    protected override void Dispose(bool disposing)
    {
        if (!_disposedValue)
        {
            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.
            _disposedValue = true;
        }

        // Call the base class implementation.
        base.Dispose(disposing);
    }
}
Public Class DerivedClassWithFinalizer
    Inherits BaseClassWithFinalizer

    ' To detect redundant calls
    Private _disposedValue As Boolean

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

    ' Protected implementation of Dispose pattern.
    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        If Not _disposedValue 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.
            _disposedValue = True
        End If

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

Zie ook