W .NET, w jakich okolicznościach powinienem użyć GC.SuppressFinalize()
?
Jakie korzyści daje mi ta metoda?
W .NET, w jakich okolicznościach powinienem użyć GC.SuppressFinalize()
?
Jakie korzyści daje mi ta metoda?
Odpowiedzi:
SuppressFinalize
powinien być wywoływany tylko przez klasę, która ma finalizator. Informuje Garbage Collector (GC), że this
obiekt został w pełni wyczyszczony.
Zalecany IDisposable
wzór w przypadku finalizatora to:
public class MyClass : IDisposable
{
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// called via myClass.Dispose().
// OK to use any private object references
}
// Release unmanaged resources.
// Set large fields to null.
disposed = true;
}
}
public void Dispose() // Implement IDisposable
{
Dispose(true);
GC.SuppressFinalize(this);
}
~MyClass() // the finalizer
{
Dispose(false);
}
}
Zwykle CLR śledzi obiekty za pomocą finalizatora podczas ich tworzenia (co powoduje, że ich tworzenie jest droższe). SuppressFinalize
informuje GC, że obiekt został poprawnie wyczyszczony i nie musi wchodzić do kolejki finalizatora. Wygląda jak niszczyciel C ++, ale nie działa jak jeden.
SuppressFinalize
Optymalizacja nie jest trywialne, jak twoi obiekty mogą żyć długo czekać w kolejce finalizatora. Nie ulegaj pokusie, aby przywoływać SuppressFinalize
inne przedmioty. To poważna wada, która czeka.
Wytyczne projektowe informują nas, że finalizator nie jest konieczny, jeśli obiekt jest implementowany IDisposable
, ale jeśli masz finalizator, powinieneś wdrożyć go, IDisposable
aby umożliwić deterministyczne porządkowanie swojej klasy.
Przez większość czasu powinieneś być w stanie uciec od IDisposable
czyszczenia zasobów. Finalizator powinien być potrzebny tylko wtedy, gdy obiekt utrzymuje niezarządzane zasoby i musisz zagwarantować, że te zasoby zostaną oczyszczone.
Uwaga: Czasami koderzy dodają finalizator do debugowania kompilacji własnych IDisposable
klas w celu przetestowania, czy kod IDisposable
poprawnie pozbył się swojego obiektu.
public void Dispose() // Implement IDisposable
{
Dispose(true);
#if DEBUG
GC.SuppressFinalize(this);
#endif
}
#if DEBUG
~MyClass() // the finalizer
{
Dispose(false);
}
#endif
IDisposable
nie jest sealed
, powinna zawierać wywołanie, GC.SuppressFinalize(this)
nawet jeśli nie zawiera finalizatora zdefiniowanego przez użytkownika . Jest to konieczne, aby zapewnić właściwą semantykę dla typów pochodnych, które dodają finalizator zdefiniowany przez użytkownika, ale tylko zastępują chronioną Dispose(bool)
metodę.
sealed
Dla klas pochodnych ważne jest, aby nie być wspomnianym przez @SamHarwell. CodeAnalysis daje ok. 1816 + ca1063, gdy klasa nie jest zapieczętowana, ale klasy zapieczętowane są w porządku bez SuppressFinalize
.
SupressFinalize
informuje system, że jakakolwiek praca wykonana w finalizatorze została już wykonana, więc finalizator nie musi być wywoływany. Z dokumentów .NET:
Obiekty, które implementują interfejs IDisposable, mogą wywoływać tę metodę z metody IDisposable.Dispose, aby uniemożliwić śmieciarzowi wywołanie Object.Finalizuj na obiekcie, który go nie wymaga.
Ogólnie rzecz biorąc, większość Dispose()
metod powinna być w stanie wywołać GC.SupressFinalize()
, ponieważ powinna wyczyścić wszystko, co zostanie wyczyszczone w finalizatorze.
SupressFinalize
jest po prostu czymś, co zapewnia optymalizację, która pozwala systemowi nie zawracać sobie głowy kolejkowaniem obiektu do wątku finalizatora. Prawidłowo napisany Dispose()
/ finalizator powinien działać poprawnie z lub bez połączenia z GC.SupressFinalize()
.
Metodę tę należy wywołać na Dispose
metodzie obiektów, która implementuje IDisposable
, w ten sposób GC nie wywołałby finalizatora innym razem, gdyby ktoś wywołał Dispose
metodę.
Patrz: Metoda GC.SuppressFinalize (Object) - Dokumenty Microsoft
Dispose(true);
GC.SuppressFinalize(this);
Jeśli obiekt ma finalizator, .net umieścił odwołanie w kolejce finalizacji.
Ponieważ mamy wywołanie Dispose(ture)
, jest to jasny obiekt, więc nie potrzebujemy kolejki finalizacji, aby wykonać tę pracę.
Więc wywołaj GC.SuppressFinalize(this)
odwołanie odwołania w kolejce finalizacji.
Jeśli klasa, czy coś pochodzi od niego, może trzymać ostatni koncert odniesienie do obiektu z finalizatora, następnie albo GC.SuppressFinalize(this)
czy GC.KeepAlive(this)
należy wezwać na obiekcie po każdej operacji, które mogłyby mieć negatywny wpływ tego finalizatora, zapewniając w ten sposób, że won finalizator będzie działać do momentu zakończenia tej operacji.
Koszt GC.KeepAlive()
i GC.SuppressFinalize(this)
są zasadniczo takie same w każdej klasie, która nie ma finalizatora, a klasy, które mają finalizatory, powinny generalnie wywoływać GC.SuppressFinalize(this)
, więc użycie tej ostatniej funkcji jako ostatniego kroku Dispose()
może nie zawsze być konieczne, ale nie będzie mylić się.