In .NET, in quali circostanze dovrei usare GC.SuppressFinalize()
?
Quali vantaggi mi offre questo metodo?
In .NET, in quali circostanze dovrei usare GC.SuppressFinalize()
?
Quali vantaggi mi offre questo metodo?
Risposte:
SuppressFinalize
dovrebbe essere chiamato solo da una classe che ha un finalizzatore. Sta informando il Garbage Collector (GC) che l' this
oggetto è stato ripulito completamente.
Lo IDisposable
schema consigliato quando si dispone di un finalizzatore è:
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);
}
}
Normalmente, il CLR tiene sotto controllo gli oggetti con un finalizzatore quando vengono creati (rendendoli più costosi da creare). SuppressFinalize
dice al GC che l'oggetto è stato ripulito correttamente e non ha bisogno di andare nella coda del finalizzatore. Sembra un distruttore C ++, ma non si comporta come uno.
L' SuppressFinalize
ottimizzazione non è banale, poiché i tuoi oggetti possono vivere a lungo in attesa sulla coda del finalizzatore. Non essere tentato di invocare SuppressFinalize
altri oggetti, attenzione a te. È un grave difetto che attende di accadere.
Le linee guida di progettazione ci informano che un finalizzatore non è necessario se il tuo oggetto viene implementato IDisposable
, ma se hai un finalizzatore devi implementarlo IDisposable
per consentire la pulizia deterministica della tua classe.
Il più delle volte dovresti essere in grado di cavartela IDisposable
per ripulire le risorse. Dovresti aver bisogno di un finalizzatore solo quando il tuo oggetto si attacca a risorse non gestite e devi garantire che tali risorse vengano ripulite.
Nota: a volte i programmatori aggiungono un finalizzatore per eseguire il debug di build delle proprie IDisposable
classi al fine di verificare che il codice abbia disposto IDisposable
correttamente il proprio oggetto.
public void Dispose() // Implement IDisposable
{
Dispose(true);
#if DEBUG
GC.SuppressFinalize(this);
#endif
}
#if DEBUG
~MyClass() // the finalizer
{
Dispose(false);
}
#endif
IDisposable
non lo è sealed
, dovrebbe includere la chiamata GC.SuppressFinalize(this)
anche se non include un finalizzatore definito dall'utente . Ciò è necessario per garantire una semantica corretta per i tipi derivati che aggiungono un finalizzatore definito dall'utente ma sovrascrivono solo il Dispose(bool)
metodo protetto .
sealed
menzionato da @SamHarwell è importante, per le classi derivate. CodeAnalysis produce ca1816 + ca1063 quando la classe non è sigillata, ma le classi sigillate vanno bene senza SuppressFinalize
.
SupressFinalize
dice al sistema che qualsiasi lavoro sarebbe stato fatto nel finalizzatore è già stato fatto, quindi il finalizzatore non ha bisogno di essere chiamato. Dai documenti .NET:
Gli oggetti che implementano l'interfaccia IDisposable possono chiamare questo metodo dal metodo IDisposable.Dispose per impedire al garbage collector di chiamare Object.Finalize su un oggetto che non lo richiede.
In generale, quasi tutti i Dispose()
metodi dovrebbero essere in grado di chiamare GC.SupressFinalize()
, perché dovrebbero ripulire tutto ciò che verrebbe ripulito nel finalizzatore.
SupressFinalize
è solo qualcosa che fornisce un'ottimizzazione che consente al sistema di non disturbare l'accodamento dell'oggetto al thread del finalizzatore. Un Dispose()
finalizzatore / scritto correttamente dovrebbe funzionare correttamente con o senza una chiamata a GC.SupressFinalize()
.
Quel metodo deve essere chiamato sul Dispose
metodo degli oggetti che implementano il IDisposable
, in questo modo il GC non chiamerebbe il finalizzatore un'altra volta se qualcuno chiama il Dispose
metodo.
Vedere: Metodo GC.SuppressFinalize (Object) - Microsoft Docs
Dispose(true);
GC.SuppressFinalize(this);
Se l'oggetto ha un finalizzatore, .net inserisce un riferimento nella coda di finalizzazione.
Dato che abbiamo chiamato Dispose(ture)
, cancella l'oggetto, quindi non abbiamo bisogno di una coda di finalizzazione per fare questo lavoro.
Quindi chiama GC.SuppressFinalize(this)
rimuovi riferimento nella coda di finalizzazione.
Se una classe, o qualsiasi cosa derivata da essa, potrebbe contenere l'ultimo riferimento attivo a un oggetto con un finalizzatore, allora uno GC.SuppressFinalize(this)
o GC.KeepAlive(this)
dovrebbe essere chiamato sull'oggetto dopo qualsiasi operazione che potrebbe essere influenzata negativamente da quel finalizzatore, assicurando così che il finalizzatore abbia vinto fino a quando l'operazione non è stata completata.
Il costo di GC.KeepAlive()
e GC.SuppressFinalize(this)
sono essenzialmente gli stessi in qualsiasi classe che non ha un finalizzatore, e le classi che hanno finalizzatori dovrebbero generalmente chiamare GC.SuppressFinalize(this)
, quindi l'uso di quest'ultima funzione come ultimo passaggio Dispose()
potrebbe non essere sempre necessario, ma non lo farà sbagliarsi.