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 ärtrue
. 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 metodenDispose(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 NotInheritable
en 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å anropabase.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)
medfalse
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