Dans .NET, dans quelles circonstances dois-je utiliser GC.SuppressFinalize()
?
Quels sont les avantages de cette méthode?
SuppressFinalize ne doit être appelé que par une classe disposant d'un finaliseur. Il informe le collecteur de déchets (GC) que l'objet this
a été entièrement nettoyé.
Le modèle IDisposable recommandé avec un finaliseur est:
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);
}
}
Normalement, le CLR garde un œil sur les objets avec un finaliseur lors de leur création (ce qui les rend plus coûteux à créer). SuppressFinalize indique au CPG que l'objet a été nettoyé correctement et qu'il n'est pas nécessaire de le placer dans la file d'attente du finaliseur. Cela ressemble à un destructeur C++, mais ne fait rien.
L'optimisation SuppressFinalize n'est pas triviale, car vos objets peuvent attendre longtemps dans la file d'attente du finaliseur. Ne soyez pas tenté d'appeler SuppressFinalize sur d'autres objets que vous. C'est un grave défaut qui attend de se produire.
Les directives de conception nous informent qu'un finaliseur n'est pas nécessaire si votre objet implémente IDisposable, mais si vous avez un finaliseur, vous devez implémenter IDisposable pour permettre le nettoyage déterministe de votre classe.
La plupart du temps, vous devriez pouvoir vous en tirer avec la possibilité de nettoyer les ressources. Vous ne devriez avoir besoin d'un finaliseur que lorsque votre objet contient des ressources non gérées et que vous devez vous assurer que ces ressources sont nettoyées.
Remarque: Parfois, les codeurs ajouteront un finaliseur pour déboguer les versions de leurs propres classes IDisposable afin de vérifier que le code a correctement supprimé leur objet IDisposable.
public void Dispose() // Implement IDisposable
{
Dispose(true);
#if DEBUG
GC.SuppressFinalize(this);
#endif
}
#if DEBUG
~MyClass() // the finalizer
{
Dispose(false);
}
#endif
vous dites au système que tout le travail qui aurait été fait dans le finaliseur a déjà été fait, de sorte que le finaliseur n'a pas besoin d'être appelé. À partir de la documentation .NET:
Les objets qui implémentent l'interface IDisposable peuvent appeler cette méthode à partir de la méthode IDisposable.Dispose pour empêcher le récupérateur de place d'appeler Object.Finalize sur un objet qui n'en a pas besoin.
En général, la plupart des méthodes Dispose () devraient pouvoir appeler GC.SupressFinalize (), car elles devraient nettoyer tout ce qui serait nettoyé dans le finaliseur.
SupressFinalize est juste quelque chose qui fournit une optimisation qui permet au système de ne pas déranger de transmettre l'objet au thread du finaliseur. Un finisseur/Dispose () correctement écrit doit fonctionner correctement avec ou sans appel à GC.SupressFinalize ().
Cette méthode doit être appelée sur la méthode Dispose d'objets qui implémente IDisposable. Ainsi, le GC n'appellera pas le finaliseur une autre fois si quelqu'un appelle la méthode Dispose.
Voir: http://msdn.Microsoft.com/en-us/library/system.gc.suppressfinalize.aspx
Dispose(true);
GC.SuppressFinalize(this);
Si l'objet dispose d'un finaliseur, .net place une référence dans la file d'attente de finalisation
Puisque nous avons appelé Dispose (ture), il est un objet clair, nous n’avons donc pas besoin de la file d’attente de finalisation pour effectuer ce travail.
Appelez donc GC.SuppressFinalize (this), supprimez la référence dans la file d'attente de finalisation.
Si une classe, ou quelque chose qui en dérive, peut contenir la dernière référence en direct à un objet avec un finaliseur, alors soit GC.SuppressFinalize(this)
ou GC.KeepAlive(this)
doit être appelé sur l'objet après toute opération Ce finaliseur pourrait avoir un impact négatif sur le finaliseur, garantissant ainsi que le finaliseur ne fonctionnera pas avant la fin de l'opération.
Les coûts de GC.KeepAlive()
et GC.SuppressFinalize(this)
sont essentiellement les mêmes dans toutes les classes dépourvues de finaliseur, et les classes disposant de finaliseurs doivent généralement appeler GC.SuppressFinalize(this)
, donc utiliser cette dernière fonction comme dernière étape de Dispose()
n'est peut-être pas toujours nécessaire, mais ce ne sera pas faux.