J'ai vu quelques questions sur les finaliseurs et IDisposable, stackoverflow devrait également avoir quelque chose sur GC.SupressFinalize et les références faibles
Sam Saffron
Je ne pense pas que les références faibles fassent grand-chose pour les finaliseurs - vous devriez peut-être poster une question plus directe à leur sujet.
Michael Burr
Yerp, je voulais publier une question séparée sur les références faibles, tout cela peut être lié lorsque vous créez des pools d'objets. Je devrais également poser une question sur la renaissance de l'objet ala ReRegisterForFinalize
Sam Saffron
Réponses:
296
SuppressFinalizene doit être appelé que par une classe dotée d'un finaliseur. Il informe le Garbage Collector (GC) que l' thisobjet a été entièrement nettoyé.
Le IDisposablemodèle recommandé lorsque vous avez un finaliseur est:
publicclassMyClass:IDisposable{privatebool disposed =false;protectedvirtualvoidDispose(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;}}publicvoidDispose()// 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). SuppressFinalizeindique 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' SuppressFinalizeoptimisation n'est pas anodine, car vos objets peuvent vivre longtemps dans la file d'attente du finaliseur. Ne soyez pas tenté d'appeler SuppressFinalized'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 IDisposablepour permettre le nettoyage déterministe de votre classe.
La plupart du temps, vous devriez pouvoir vous en sortir IDisposablepour 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 IDisposableclasses afin de tester que le code a IDisposablecorrectement disposé leur objet.
publicvoidDispose()// Implement IDisposable{Dispose(true);#if DEBUG
GC.SuppressFinalize(this);#endif}#if DEBUG~MyClass()// the finalizer{Dispose(false);}#endif
Dans le premier extrait de code, je poste simplement à quoi ressemble le modèle IDisposable + finalizer recommandé. Le débogage du code est bon, mais il peut être gênant. .. Je ne peux que recommander d'éviter les finaliseurs, sauf pour les classes qui ont des ressources non gérées. L'écriture de code de finaliseur sécurisé n'est pas anodine.
Robert Paulson
1
Salut, Pourquoi devons-nous appeler Dispose avec false comme paramètre à partir du finaliseur? Que se passe-t-il si le disposer n'a jamais été appelé et qu'il ne dispose pas? Et si nous vérifions simplement si l'objet a été éliminé ou non et faisons le nettoyage proprement dit.
Dreamer
3
@Dreamer - cela dépend de votre implémentation. En général, vous voulez savoir si Dispose est appelé par le finaliseur par rapport à l'implémentation IDisposable.Dispose (). S'il est appelé depuis le finaliseur, vous devez supposer que les références privées ne sont plus valides et que vous ne pouvez vraiment pas faire grand-chose. Si toutefois appelé depuis IDisposable.Dispose (), vous savez que les références sont toujours valides.
Robert Paulson
32
Si la classe implémentant IDisposablene 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 .
Sam Harwell
1
Ne pas être sealedcomme 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.
dashesy
38
SupressFinalizeindique 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.
SupressFinalizeest 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 Disposemé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 Disposeméthode.
Je pense que "Must" est faux - pas même "devrait" - C'est juste que dans certains scénarios, vous pouvez éliminer la surcharge de la mise en file d'attente / finalisation de l'objet.
Basic
1
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.
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: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'appelerSuppressFinalize
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émenterIDisposable
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 aIDisposable
correctement disposé leur objet.la source
IDisposable
ne l'est passealed
, 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 laDispose(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 sansSuppressFinalize
.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:En général, la plupart des
Dispose()
méthodes devraient pouvoir appelerGC.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édacteurDispose()
/ rédacteur correctement écrit devrait fonctionner correctement avec ou sans appel àGC.SupressFinalize()
.la source
Cette méthode doit être appelée sur la
Dispose
méthode des objets qui implémente leIDisposable
, de cette façon, le GC n'appellerait pas le finaliseur une autre fois si quelqu'un appelle laDispose
méthode.Voir: GC.SuppressFinalize (Object), méthode - Microsoft Docs
la source
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.la source
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)
ouGC.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()
etGC.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 appelerGC.SuppressFinalize(this)
, donc utiliser cette dernière fonction comme dernière étape deDispose()
peut ne pas toujours être nécessaire, mais ce ne sera pas le cas se tromper.la source