Dela via


Implementera en avyttra-metod

Metoden Dispose implementeras främst för att frigöra ohanterade resurser. När du arbetar med instansmedlemmar som är IDisposable implementeringar är det vanligt att överlappa Dispose anrop. Det finns andra orsaker till att implementera Dispose, till exempel för att frigöra minne som allokerats, ta bort ett objekt som lades till i en samling eller signalera lanseringen av ett lås som hämtades.

.NET-skräpinsamlaren allokerar eller frigör inte ohanterat minne. Mönstret för att exponera ett objekt, som kallas mönstret för bortskaffande, medför ordning på livslängden för ett objekt. Mönstret för bortskaffning används för objekt som implementerar IDisposable gränssnittet. Det här mönstret är vanligt när du interagerar med fil- och rörhandtag, registerhandtag, väntehandtag eller pekare till block med ohanterat minne, eftersom skräpinsamlaren inte kan frigöra ohanterade objekt.

För att säkerställa att resurserna alltid rensas på rätt sätt bör en Dispose metod vara idempotent, så att den kan anropas flera gånger utan att utlösa ett undantag. Dessutom bör efterföljande anrop av Dispose inte göra någonting.

Kodexemplet för GC.KeepAlive metoden visar hur skräpinsamling kan orsaka att en slutförare körs medan en ohanterad referens till objektet eller dess medlemmar fortfarande används. Det kan vara klokt att använda GC.KeepAlive för att göra objektet olämpligt för skräpinsamling från början av den aktuella rutinen till den punkt där den här metoden anropas.

Dricks

När det gäller beroendeinmatning hanteras tjänstlivslängden implicit för din räkning när du registrerar tjänster i en IServiceCollection. Och IServiceProvider motsvarande orkestrera IHost resursrensning. Mer specifikt tas implementeringarna av IDisposable och IAsyncDisposable bort i slutet av den angivna livslängden.

Mer information finns i Beroendeinmatning i .NET.

Valv referenser

Att skriva kod för ett objekts finaliserare är en komplex uppgift som kan orsaka problem om det inte görs korrekt. Därför rekommenderar vi att du skapar System.Runtime.InteropServices.SafeHandle objekt i stället för att implementera en finalizer.

A System.Runtime.InteropServices.SafeHandle är en abstrakt hanterad typ som omsluter en System.IntPtr som identifierar en ohanterad resurs. I Windows kan det identifiera ett handtag och på Unix en filbeskrivning. SafeHandle Innehåller all logik som krävs för att säkerställa att den här resursen släpps en gång och bara en gång, antingen när den SafeHandle tas bort eller när alla referenser till SafeHandle har tagits bort och instansen SafeHandle har slutförts.

System.Runtime.InteropServices.SafeHandle är en abstrakt basklass. Härledda klasser tillhandahåller specifika instanser för olika typer av handtag. Dessa härledda klasser verifierar vilka värden för System.IntPtr som anses vara ogiltiga och hur du faktiskt frigör handtaget. Härleds till exempel SafeFileHandle från SafeHandle till wrap IntPtrs som identifierar öppna filreferenser/deskriptorer och åsidosätter dess SafeHandle.ReleaseHandle() metod för att stänga den close (via funktionen på Unix eller CloseHandle funktionen i Windows). De flesta API:er i .NET-bibliotek som skapar en ohanterad resurs omsluter den i en SafeHandle och returnerar den SafeHandle till dig efter behov, i stället för att lämna tillbaka rådatapekaren. I situationer där du interagerar med en ohanterad komponent och hämtar en IntPtr för en ohanterad resurs kan du skapa en egen SafeHandle typ för att omsluta den. Därför behöver få icke-typerSafeHandle implementera finalizers. De flesta implementeringar av engångsmönster omsluter bara andra hanterade resurser, varav vissa kan vara SafeHandle objekt.

Följande härledda klasser i Microsoft.Win32.SafeHandles namnområdet ger säkra referenser.

Klass Resurser som den innehåller
SafeFileHandle
SafeMemoryMappedFileHandle
SafePipeHandle
Filer, minnesmappade filer och rör
SafeMemoryMappedViewHandle Minnesvyer
SafeNCryptKeyHandle
SafeNCryptProviderHandle
SafeNCryptSecretHandle
Kryptografikonstruktioner
SafeRegistryHandle Registernycklar
SafeWaitHandle Väntehandtag

Dispose() och Dispose(bool)

Gränssnittet IDisposable kräver implementering av en enda parameterlös metod, Dispose. Dessutom bör alla icke-förseglade klasser ha en Dispose(bool) överlagringsmetod.

Metodsignaturer är:

  • public icke-virtuell (NotOverridable i Visual Basic) (IDisposable.Dispose implementering).
  • protected virtual (Overridable i Visual Basic) Dispose(bool).

Metoden Dispose()

Eftersom metoden public, icke-virtuell (NotOverridable i Visual Basic), anropas när Dispose den inte längre behövs (av en konsument av typen), är dess syfte att frigöra ohanterade resurser, utföra allmän rensning och att ange att finalizern, om den finns, inte behöver köras. Att frigöra det faktiska minnet som är associerat med ett hanterat objekt är alltid domänen för skräpinsamlaren. På grund av detta har den en standardimplementering:

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

Metoden Dispose utför all objektrensning, så skräpinsamlaren behöver inte längre anropa objektens åsidosättning Object.Finalize . Därför förhindrar anropet SuppressFinalize till metoden skräpinsamlaren från att köra finalizern. Om typen inte har någon finalizer har anropet till GC.SuppressFinalize ingen effekt. Den faktiska rensningen utförs av metodens Dispose(bool) överlagring.

Överlagring av metoden Dispose(bool)

I överlagringen är parametern disposing en Boolean som anger om metodanropet kommer från en Dispose metod (dess värde är true) eller från en finalizer (dess värde är 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

Viktigt!

Parametern disposing ska vara false när den anropas från en slutversion och true när den anropas IDisposable.Dispose från metoden. Med andra ord är true det när deterministiskt anropas och false när icke-deterministiskt anropas.

Metodens brödtext består av tre kodblock:

  • Ett block för villkorlig retur om objektet redan har tagits bort.

  • Ett block som frigör ohanterade resurser. Det här blocket körs oavsett värdet för parametern disposing .

  • Ett villkorligt block som frigör hanterade resurser. Det här blocket körs om värdet disposing för är true. De hanterade resurser som frigörs kan vara:

    • Hanterade objekt som implementerar IDisposable. Det villkorliga blocket kan användas för att anropa implementeringen Dispose (kaskaderering). Om du har använt en härledd klass av System.Runtime.InteropServices.SafeHandle för att omsluta din ohanterade resurs bör du anropa implementeringen SafeHandle.Dispose() här.

    • Hanterade objekt som förbrukar stora mängder minne eller förbrukar knappa resurser. Tilldela stora referenser till hanterade objekt för null att göra dem mer benägna att vara oåtkomliga. Detta frigör dem snabbare än om de inte hade återtagits obestämt.

Om metodanropet kommer från en finalizer ska endast den kod som frigör ohanterade resurser köras. Implementeraren ansvarar för att se till att den falska sökvägen inte interagerar med hanterade objekt som kan ha tagits bort. Detta är viktigt eftersom ordningen i vilken skräpinsamlaren kasserar hanterade objekt under slutförande är icke-terministisk.

Kaskade anrop för bortskaffande

Om klassen äger ett fält eller en egenskap och dess typ implementerar IDisposablebör den innehållande klassen också implementera IDisposable. En klass som instansierar en IDisposable implementering och lagrar den som instansmedlem ansvarar också för rensningen. Detta säkerställer att de refererade disponibla typerna ges möjlighet att deterministiskt utföra rensning via Dispose metoden. I följande exempel är sealed klassen (eller NotInheritable i 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

Dricks

  • Om klassen har ett fält eller en IDisposable egenskap men inte äger det, vilket innebär att klassen inte skapar objektet, behöver klassen inte implementera IDisposable.
  • Det finns fall då du kanske vill utföra null-checka in en finalizer (som innehåller metoden Dispose(false) som anropas av en finalator). En av de främsta orsakerna är om du är osäker på om instansen har initierats helt (till exempel kan ett undantag utlösas i en konstruktor).

Implementera mönstret för bortskaffning

Alla icke-förseglade klasser (eller Visual Basic-klasser som inte har ändrats som ) bör betraktas som NotInheritableen potentiell basklass, eftersom de kan ärvas. Om du implementerar mönstret för bortskaffning för eventuella basklasser måste du ange följande:

  • En Dispose implementering som anropar Dispose(bool) metoden.
  • En Dispose(bool) metod som utför den faktiska rensningen.
  • Antingen omsluter en klass som härleds från SafeHandle den ohanterade resursen Object.Finalize (rekommenderas) eller en åsidosättning till metoden. Klassen SafeHandle innehåller en finalizer, så du behöver inte skriva en själv.

Viktigt!

Det är möjligt för en basklass att endast referera till hanterade objekt och implementera mönstret för bortskaffning. I dessa fall är en finalator onödig. En finalizer krävs bara om du direkt refererar till ohanterade resurser.

Här är ett allmänt exempel på hur du implementerar mönstret för bortskaffning för en basklass som använder ett säkert handtag.

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

Kommentar

I föregående exempel används ett SafeFileHandle objekt för att illustrera mönstret. Alla objekt som härleds från SafeHandle kan användas i stället. Observera att exemplet inte instansierar objektet SafeFileHandle korrekt.

Här är det allmänna mönstret för att implementera mönstret för bortskaffning för en basklass som åsidosätter Object.Finalize.

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

Dricks

I C# implementerar du en slutförande genom att tillhandahålla en finalator, inte genom att Object.Finalizeåsidosätta . I Visual Basic skapar du en finalizer med Protected Overrides Sub Finalize().

Implementera mönstret för bortskaffning för en härledd klass

En klass som härleds från en klass som implementerar IDisposable gränssnittet bör inte implementera IDisposable, eftersom basklassimplementeringen av IDisposable.Dispose ärvs av dess härledda klasser. För att rensa en härledd klass anger du i stället följande:

  • En protected override void Dispose(bool) metod som åsidosätter basklassmetoden och utför den faktiska rensningen av den härledda klassen. Den här metoden måste också anropa base.Dispose(bool) metoden (MyBase.Dispose(bool) i Visual Basic) som skickar den disponerande statusen (bool disposing parametern) som ett argument.
  • Antingen omsluter en klass som härleds från SafeHandle den ohanterade resursen Object.Finalize (rekommenderas) eller en åsidosättning till metoden. Klassen SafeHandle innehåller en finalizer som gör att du inte behöver koda en. Om du anger en finalizer måste den anropa överlagringen Dispose(bool) med false argumentet .

Här är ett exempel på det allmänna mönstret för att implementera mönstret för hanteringen för en härledd klass som använder ett säkert handtag:

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

Kommentar

I föregående exempel används ett SafeFileHandle objekt för att illustrera mönstret. Alla objekt som härleds från SafeHandle kan användas i stället. Observera att exemplet inte instansierar objektet SafeFileHandle korrekt.

Här är det allmänna mönstret för att implementera mönstret för bortskaffning för en härledd klass som åsidosätter Object.Finalize:

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

Se även