C# Language
Interfejs IDisposable
Szukaj…
Uwagi
Klienci klasy implementujący
IDisposable
muszą upewnić się, że wywołują metodęDispose
po zakończeniu używania obiektu. W CLR nie ma niczego, co bezpośrednio przeszukuje obiekty w celu wywołania metodyDispose
.Implementacja finalizatora nie jest konieczna, jeśli obiekt zawiera tylko zasoby zarządzane. Pamiętaj, aby wywołać
Dispose
na wszystkich obiektach używanych przez twoją klasę podczas implementacji własnej metodyDispose
.Zaleca się, aby uczynić klasę bezpieczną przed wieloma wywołaniami
Dispose
, chociaż najlepiej byłoby wywołać ją tylko raz. Można to osiągnąć przez dodanieprivate bool
zmiennejprivate bool
do swojej klasy i ustawienie wartościtrue
po uruchomieniu metodyDispose
.
W klasie zawierającej tylko zarządzane zasoby
Zarządzane zasoby to zasoby, o których kolektor śmieci środowiska wykonawczego jest świadomy i pod kontrolą. W BCL dostępnych jest wiele klas, na przykład SqlConnection
która jest klasą opakowania dla niezarządzanego zasobu. Klasy te już implementują interfejs IDisposable
- to zależy od twojego kodu, aby je wyczyścić po zakończeniu.
Implementacja finalizatora nie jest konieczna, jeśli klasa zawiera tylko zasoby zarządzane.
public class ObjectWithManagedResourcesOnly : IDisposable
{
private SqlConnection sqlConnection = new SqlConnection();
public void Dispose()
{
sqlConnection.Dispose();
}
}
W klasie z zarządzanymi i niezarządzanymi zasobami
Ważne jest, aby finalizacja ignorowała zarządzane zasoby. Finalizator działa na innym wątku - możliwe jest, że zarządzane obiekty już nie istnieją do czasu uruchomienia finalizatora. Wdrażanie chronionej metody Dispose(bool)
jest powszechną praktyką zapewniającą, że zasoby zarządzane nie mają wywoływanej metody Dispose
z finalizatora.
public class ManagedAndUnmanagedObject : IDisposable
{
private SqlConnection sqlConnection = new SqlConnection();
private UnmanagedHandle unmanagedHandle = Win32.SomeUnmanagedResource();
private bool disposed;
public void Dispose()
{
Dispose(true); // client called dispose
GC.SuppressFinalize(this); // tell the GC to not execute the Finalizer
}
protected virtual void Dispose(bool disposeManaged)
{
if (!disposed)
{
if (disposeManaged)
{
if (sqlConnection != null)
{
sqlConnection.Dispose();
}
}
unmanagedHandle.Release();
disposed = true;
}
}
~ManagedAndUnmanagedObject()
{
Dispose(false);
}
}
IDisposable, Dispose
.NET Framework definiuje interfejs dla typów wymagających metody usuwania:
public interface IDisposable
{
void Dispose();
}
Dispose()
służy przede wszystkim do czyszczenia zasobów, takich jak niezarządzane odwołania. Jednak może być również przydatne wymuszenie dysponowania innymi zasobami, nawet jeśli są one zarządzane. Zamiast czekać, aż GC ostatecznie wyczyści również połączenie z bazą danych, możesz upewnić się, że zostało to zrobione we własnej implementacji Dispose()
.
public void Dispose()
{
if (null != this.CurrentDatabaseConnection)
{
this.CurrentDatabaseConnection.Dispose();
this.CurrentDatabaseConnection = null;
}
}
Gdy potrzebujesz bezpośredniego dostępu do niezarządzanych zasobów, takich jak niezarządzane wskaźniki lub zasoby win32, utwórz klasę dziedziczącą po SafeHandle
i użyj do tego konwencji / narzędzi tej klasy.
W odziedziczonej klasie z zarządzanymi zasobami
Dość często zdarza się, że możesz stworzyć klasę, która implementuje IDisposable
, a następnie wyprowadzić klasy, które również zawierają zasoby zarządzane. Zaleca się oznaczenie metody Dispose
virtual
słowem kluczowym, aby klienci mieli możliwość wyczyszczenia wszelkich posiadanych zasobów.
public class Parent : IDisposable
{
private ManagedResource parentManagedResource = new ManagedResource();
public virtual void Dispose()
{
if (parentManagedResource != null)
{
parentManagedResource.Dispose();
}
}
}
public class Child : Parent
{
private ManagedResource childManagedResource = new ManagedResource();
public override void Dispose()
{
if (childManagedResource != null)
{
childManagedResource.Dispose();
}
//clean up the parent's resources
base.Dispose();
}
}
za pomocą słowa kluczowego
Gdy obiekt implementuje interfejs IDisposable
, można go utworzyć w składni using
:
using (var foo = new Foo())
{
// do foo stuff
} // when it reaches here foo.Dispose() will get called
public class Foo : IDisposable
{
public void Dispose()
{
Console.WriteLine("dispose called");
}
}
using
jest cukrem syntatycznym do blokowania try/finally
; powyższe użycie z grubsza przełoży się na:
{
var foo = new Foo();
try
{
// do foo stuff
}
finally
{
if (foo != null)
((IDisposable)foo).Dispose();
}
}