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:
SuppressFinalizepowinien być wywoływany tylko przez klasę, która ma finalizator. Informuje Garbage Collector (GC), że thisobiekt został w pełni wyczyszczony.
Zalecany IDisposablewzó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). SuppressFinalizeinformuje GC, że obiekt został poprawnie wyczyszczony i nie musi wchodzić do kolejki finalizatora. Wygląda jak niszczyciel C ++, ale nie działa jak jeden.
SuppressFinalizeOptymalizacja nie jest trywialne, jak twoi obiekty mogą żyć długo czekać w kolejce finalizatora. Nie ulegaj pokusie, aby przywoływać SuppressFinalizeinne 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, IDisposableaby umożliwić deterministyczne porządkowanie swojej klasy.
Przez większość czasu powinieneś być w stanie uciec od IDisposableczyszczenia 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 IDisposableklas w celu przetestowania, czy kod IDisposablepoprawnie 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
IDisposablenie 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ę.
sealedDla 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.
SupressFinalizeinformuje 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.
SupressFinalizejest 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 Disposemetodzie obiektów, która implementuje IDisposable, w ten sposób GC nie wywołałby finalizatora innym razem, gdyby ktoś wywołał Disposemetodę.
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ę.