Dans .NET, dans quelles circonstances dois-je utiliser GC.SuppressFinalize()
?
Quels avantages me procurent cette méthode?
Dans .NET, dans quelles circonstances dois-je utiliser GC.SuppressFinalize()
?
Quels avantages me procurent cette méthode?
Réponses:
SuppressFinalize
ne doit être appelé que par une classe dotée d'un finaliseur. Il informe le Garbage Collector (GC) que l' this
objet a été entièrement nettoyé.
Le IDisposable
modèle recommandé lorsque vous avez 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 onglet sur les objets avec un finaliseur lorsqu'ils sont créés (ce qui les rend plus coûteux à créer). SuppressFinalize
indique au GC que l'objet a été correctement nettoyé et n'a pas besoin d'aller dans la file d'attente du finaliseur. Il ressemble à un destructeur C ++, mais n'agit en rien.
L' SuppressFinalize
optimisation n'est pas anodine, car vos objets peuvent vivre longtemps dans la file d'attente du finaliseur. Ne soyez pas tenté d'appeler SuppressFinalize
d'autres objets. 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 est implémenté IDisposable
, mais si vous avez un finaliseur, vous devez l'implémenter IDisposable
pour permettre le nettoyage déterministe de votre classe.
La plupart du temps, vous devriez pouvoir vous en sortir IDisposable
pour nettoyer les ressources. Vous ne devez avoir besoin d'un finaliseur que lorsque votre objet conserve des ressources non gérées et vous devez garantir que ces ressources sont nettoyées.
Remarque: Parfois, les codeurs ajoutent un finaliseur pour déboguer les versions de leurs propres IDisposable
classes afin de tester que le code a IDisposable
correctement disposé leur objet.
public void Dispose() // Implement IDisposable
{
Dispose(true);
#if DEBUG
GC.SuppressFinalize(this);
#endif
}
#if DEBUG
~MyClass() // the finalizer
{
Dispose(false);
}
#endif
IDisposable
ne l'est pas sealed
, elle doit inclure l'appel à GC.SuppressFinalize(this)
même si elle n'inclut pas de finaliseur défini par l'utilisateur . Cela est nécessaire pour garantir une sémantique correcte pour les types dérivés qui ajoutent un finaliseur défini par l'utilisateur mais remplacent uniquement la Dispose(bool)
méthode protégée .
sealed
comme mentionné par @SamHarwell est important pour les classes dérivées. CodeAnalysis résulte en ca1816 + ca1063 lorsque la classe n'est pas scellée, mais les classes scellées sont bien sans SuppressFinalize
.
SupressFinalize
indique au système que tout travail qui aurait été effectué dans le finaliseur a déjà été fait, il n'est donc pas nécessaire d'appeler le finaliseur. À partir des documents .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 garbage collector d'appeler Object.Finalize sur un objet qui n'en a pas besoin.
En général, la plupart des Dispose()
méthodes 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 la mise en file d'attente de l'objet sur le thread de finalisation. Un rédacteur Dispose()
/ rédacteur correctement écrit devrait fonctionner correctement avec ou sans appel à GC.SupressFinalize()
.
Cette méthode doit être appelée sur la Dispose
méthode des objets qui implémente le IDisposable
, de cette façon, le GC n'appellerait pas le finaliseur une autre fois si quelqu'un appelle la Dispose
méthode.
Voir: GC.SuppressFinalize (Object), méthode - Microsoft Docs
Dispose(true);
GC.SuppressFinalize(this);
Si l'objet a un finaliseur, .net met une référence dans la file d'attente de finalisation.
Puisque nous avons appelé Dispose(ture)
, il efface l'objet, nous n'avons donc pas besoin de la file d'attente de finalisation pour faire ce travail.
Appelez donc GC.SuppressFinalize(this)
remove reference 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 GC.SuppressFinalize(this)
ou GC.KeepAlive(this)
doit être appelé sur l'objet après toute opération qui pourrait être affectée par ce finaliseur, garantissant ainsi que le finaliseur l'emporte ne fonctionnera qu'après la fin de cette opération.
Le coût de GC.KeepAlive()
et GC.SuppressFinalize(this)
est essentiellement le même dans toute classe qui n'a pas de finaliseur, et les classes qui ont des finaliseurs devraient généralement appeler GC.SuppressFinalize(this)
, donc utiliser cette dernière fonction comme dernière étape de Dispose()
peut ne pas toujours être nécessaire, mais ce ne sera pas le cas se tromper.