Implementace metody Dispose
Metoda Dispose se primárně implementuje pro uvolnění nespravovaných prostředků. Při práci s členy instance, které jsou IDisposable implementacemi, je běžné kaskádové Dispose volání. Existují další důvody pro implementaci Dispose, například uvolnit paměť, která byla přidělena, odebrat položku přidanou do kolekce nebo signalizovat uvolnění zámku, který byl získán.
Uvolňování paměti .NET nepřiděluje ani nevyvolá nespravovanou paměť. Vzor pro likvidaci objektu, označovaný jako vzor dispose, ukládá pořadí na dobu životnosti objektu. Model Dispose se používá pro objekty, které implementují IDisposable rozhraní. Tento vzor je běžný při interakci s popisovači souborů a kanálu, popisovačů registru, popisovačů čekání nebo ukazatelů na bloky nespravované paměti, protože uvolňování paměti nemůže uvolnit nespravované objekty.
Aby bylo možné zajistit, aby se prostředky vždy správně vyčistily, Dispose měla by být metoda idempotentní, aby byla volána vícekrát bez vyvolání výjimky. Další vyvolání Dispose by navíc nemělo dělat nic.
Příklad kódu uvedený pro metodu GC.KeepAlive ukazuje, jak může uvolňování paměti způsobit, že se finalizátor spustí, zatímco nespravovaný odkaz na objekt nebo jeho členy je stále používán. Použití objektu může být GC.KeepAlive vhodné k tomu, aby byl objekt způsobilý pro uvolňování paměti od začátku aktuální rutiny do bodu, kde je volána tato metoda.
Tip
Pokud jde o injektáž závislostí, při registraci služeb v rámci IServiceCollectionslužby je životnost služby spravována implicitně vaším jménem. Vyčištění IServiceProvider prostředků a odpovídající IHost orchestrace. Konkrétně implementace IDisposable a IAsyncDisposable jsou řádně uvolněny na konci jejich zadané životnosti.
Další informace naleznete v tématu Injektáž závislostí v .NET.
Sejf úchyty
Psaní kódu pro finalizační metodu objektu je složitý úkol, který může způsobit problémy, není-li prováděn správně. Proto doporučujeme, abyste místo implementace finalizátoru vytvořili System.Runtime.InteropServices.SafeHandle objekty.
A System.Runtime.InteropServices.SafeHandle je abstraktní spravovaný typ, který zabalí System.IntPtr nespravovaný prostředek. Ve Windows může popisovač identifikovat popisovač a v unixovém popisovači. Poskytuje SafeHandle
veškerou logiku potřebnou k tomu, aby byl tento prostředek uvolněn jednou a pouze jednou, a to buď při SafeHandle
vyřazení, nebo při vyřazení všech odkazů na SafeHandle
tento prostředek a SafeHandle
dokončení instance.
Jedná se System.Runtime.InteropServices.SafeHandle o abstraktní základní třídu. Odvozené třídy poskytují konkrétní instance pro různé druhy popisovačů. Tyto odvozené třídy ověřují, jaké hodnoty jsou System.IntPtr považovány za neplatné a jak skutečně uvolnit popisovač. Například odvozuje od zabaleníIntPtrs
, SafeFileHandle které identifikují otevřené popisovače nebo popisovače souborů, a přepíše jeho SafeHandle.ReleaseHandle() metodu tak, aby ji zavřela (prostřednictvím close
funkce v systému Unix nebo CloseHandle
SafeHandle
funkce ve Windows). Většina rozhraní API v knihovnách .NET, které vytvářejí nespravovaný prostředek, zabalí ho do SafeHandle
a vrátí SafeHandle
se k vám podle potřeby, a ne předání nezpracovaného ukazatele. V situacích, kdy pracujete s nespravovanou komponentou a získáte pro IntPtr
nespravovaný prostředek, můžete vytvořit vlastní SafeHandle
typ, který ji zabalí. V důsledku toho je potřeba implementovat finalizační metody několika typů, které nejsouSafeHandle
typy. Většina jednorázových implementací vzorů končí pouze zabalením jiných spravovaných prostředků, z nichž některé můžou být SafeHandle
objekty.
Následující odvozené třídy v Microsoft.Win32.SafeHandles oboru názvů poskytují bezpečné popisovače.
Třída | Prostředky, které obsahuje |
---|---|
SafeFileHandle SafeMemoryMappedFileHandle SafePipeHandle |
Soubory, soubory mapované v paměti a kanály |
SafeMemoryMappedViewHandle | Zobrazení paměti |
SafeNCryptKeyHandle SafeNCryptProviderHandle SafeNCryptSecretHandle |
Kryptografické konstrukce |
SafeRegistryHandle | Klíče registru |
SafeWaitHandle | Obslužné rutiny čekání |
Dispose() a Dispose(bool)
Rozhraní IDisposable vyžaduje implementaci jedné bezparametrové metody , Dispose. Také všechny nezapečetěné třídy by měly mít metodu Dispose(bool)
přetížení.
Podpisy metod jsou:
public
non-virtual (NotOverridable
v jazyce Visual Basic) (IDisposable.Dispose implementace).protected virtual
(Overridable
v jazyce Visual Basic)Dispose(bool)
.
Metoda Dispose()
public
Vzhledem k tomu, že , non-virtual (NotOverridable
v jazyce Visual Basic), parametrless Dispose
metoda je volána, když už není potřeba (příjemcem typu), jeho účelem je uvolnit nespravované prostředky, provést obecné vyčištění a indikovat, že finalizátor, pokud je k dispozici, nemusí běžet. Uvolnění skutečné paměti přidružené ke spravovanému objektu je vždy doménou uvolňování paměti. Z tohoto důvodu má standardní implementaci:
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
Metoda Dispose
provede všechny vyčištění objektu, takže uvolňování paměti už nemusí volat přepsání Object.Finalize objektů. Proto volání SuppressFinalize metody zabraňuje uvolňování paměti spuštění finalizátoru. Pokud typ nemá žádný finalizátor, volání GC.SuppressFinalize nemá žádný vliv. Skutečné vyčištění provádí Dispose(bool)
přetížení metody.
Přetížení metody Dispose(bool)
V přetížení je parametr, který označuje, disposing
zda volání metody pochází z Dispose metody (jeho hodnota ) true
nebo finalizátor (jeho hodnota je false
).Boolean
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
Důležité
Parametr disposing
by měl být false
při zavolání z finalizátoru IDisposable.Dispose a true
při zavolání z metody. Jinými slovy, je true
to, když deterministicky volána a false
kdy není deterministicky volána.
Tělo metody se skládá ze tří bloků kódu:
Blok pro podmíněný návrat, pokud je objekt již uvolněn.
Blok, který uvolní nespravované prostředky. Tento blok se spustí bez ohledu na hodnotu parametru
disposing
.Podmíněný blok, který uvolní spravované prostředky. Tento blok se spustí, pokud je
true
hodnotadisposing
. Mezi uvolněné spravované prostředky patří:Spravované objekty, které implementují IDisposable. Podmíněný blok lze použít k volání jejich Dispose implementace (kaskádová dispose). Pokud jste k zabalení nespravovaného prostředku použili odvozenou třídu System.Runtime.InteropServices.SafeHandle , měli byste sem zavolat implementaci SafeHandle.Dispose() .
Spravované objekty, které spotřebovávají velké množství paměti nebo využívají málo prostředků. Přiřaďte velké odkazy na spravované objekty, aby
null
byly s větší pravděpodobností nedostupnější. Tím se uvolní rychleji, než kdyby byly uvolněny nedeterministicky.
Pokud volání metody pochází z finalizátoru, měl by se spustit pouze kód, který uvolní nespravované prostředky. Implementátor zodpovídá za to, že nepravdivá cesta nepracuje se spravovanými objekty, které mohly být uvolněny. To je důležité, protože pořadí, ve kterém uvolňování paměti vyhazuje spravované objekty během finalizace, je nedeterministické.
Kaskádová volání dispose
Pokud vaše třída vlastní pole nebo vlastnost a jeho typ implementuje IDisposable, obsahující třída by měla také implementovat IDisposable. Třída, která vytvoří instanci IDisposable implementace a uloží ji jako člen instance, je také zodpovědná za vyčištění. To pomáhá zajistit, aby odkazované uvolnitelné typy byly dány příležitostí deterministicky provést vyčištění prostřednictvím Dispose metody. V následujícím příkladu je sealed
třída (nebo NotInheritable
v jazyce 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
- Pokud má vaše třída IDisposable pole nebo vlastnost, ale nevlastní ho, což znamená, že třída nevytvoří objekt, pak třída nemusí implementovat IDisposable.
- Existují případy, kdy můžete chtít provést
null
kontrolu finalizátoru (která zahrnuje metoduDispose(false)
vyvolanou finalizátorem). Jedním z hlavních důvodů je, že si nejste jistí, jestli se instance plně inicializovala (například výjimka může být vyvolána v konstruktoru).
Implementace vzoru Dispose
Všechny nezapečetěné třídy (nebo třídy jazyka Visual Basic, které nejsou upraveny jako NotInheritable
) by měly být považovány za potenciální základní třídu, protože by mohly být zděděné. Pokud implementujete vzor Dispose pro libovolnou potenciální základní třídu, musíte zadat následující:
- Implementace Dispose , která volá metodu
Dispose(bool)
. - Metoda
Dispose(bool)
, která provádí skutečné vyčištění. - Buď třída odvozená z SafeHandle toho, že zabalí váš nespravovaný prostředek (doporučeno), nebo přepsání metody Object.Finalize . Třída SafeHandle poskytuje finalizační metodu, takže si ji nemusíte psát sami.
Důležité
Základní třída může odkazovat pouze na spravované objekty a implementovat vzor Dispose. V těchto případech je finalizátor zbytečný. Finalizátor se vyžaduje pouze v případě, že přímo odkazujete na nespravované prostředky.
Tady je obecný příklad implementace vzoru Dispose pro základní třídu, která používá bezpečný popisovač.
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
Poznámka:
Předchozí příklad používá SafeFileHandle objekt k ilustraci vzoru. Místo toho lze použít jakýkoli objekt odvozený z SafeHandle tohoto objektu. Všimněte si, že v příkladu není správně vytvoření instance objektu SafeFileHandle .
Zde je obecný vzor pro implementaci modelu Dispose pro základní třídu, která přepisuje 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
Tip
V jazyce C# implementujete finalizaci tím, že poskytnete finalizační metodu, nikoli přepsáním Object.Finalize. V jazyce Visual Basic vytvoříte finalizační metodou Protected Overrides Sub Finalize()
.
Implementace vzoru Dispose pro odvozenou třídu
Třída odvozená z třídy, která implementuje IDisposable rozhraní by nemělo implementovat IDisposable, protože základní třída implementace IDisposable.Dispose je zděděna jeho odvozené třídy. Pokud chcete vyčistit odvozenou třídu, zadejte následující:
- Metoda
protected override void Dispose(bool)
, která přepíše metodu základní třídy a provede skutečné vyčištění odvozené třídy. Tato metoda musí také volat metodubase.Dispose(bool)
(MyBase.Dispose(bool)
v jazyce Visual Basic), která jí předá stav vystavení (bool disposing
parametr) jako argument. - Buď třída odvozená z SafeHandle toho, že zabalí váš nespravovaný prostředek (doporučeno), nebo přepsání metody Object.Finalize . Třída SafeHandle poskytuje finalizátor, který vás uvolní od nutnosti psaní kódu. Pokud zadáte finalizátor, musí volat
Dispose(bool)
přetížení s argumentemfalse
.
Tady je příklad obecného vzoru pro implementaci vzoru Dispose pro odvozenou třídu, která používá bezpečný popisovač:
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
Poznámka:
Předchozí příklad používá SafeFileHandle objekt k ilustraci vzoru. Místo toho lze použít jakýkoli objekt odvozený z SafeHandle tohoto objektu. Všimněte si, že v příkladu není správně vytvoření instance objektu SafeFileHandle .
Tady je obecný vzor pro implementaci vzoru Dispose pro odvozenou třídu, která přepisuje 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