L'utilisation de Finaliser/méthode dispose en C#
C# 2008
J'ai travaillé sur cette question depuis un certain temps maintenant, et je suis toujours confus au sujet de l'utilisation de finaliser et de disposer de méthodes dans le code. Mes questions sont ci-dessous:
-
Je sais que nous avons seulement besoin d'un outil de finalisation, tandis que l'élimination des ressources non managées. Cependant, si il y a des ressources gérées qui font appels à des ressources non managées, serait-il encore besoin de mettre en œuvre un finaliseur?
-
Cependant, si je développe une classe qui n'utilise pas du tout non géré les ressources directement ou indirectement, dois-je mettre en œuvre les
IDisposable
pour permettre aux clients de cette classe à utiliser le 'l'aide de déclaration"?Serait-il possible de mettre en œuvre IDisposable juste pour permettre aux clients de votre classe à utiliser l'instruction à l'aide?
using(myClass objClass = new myClass()) { //Do stuff here }
-
J'ai développé ce simple code ci-dessous pour illustrer le Finaliser/disposer d'utilisation:
public class NoGateway : IDisposable { private WebClient wc = null; public NoGateway() { wc = new WebClient(); wc.DownloadStringCompleted += wc_DownloadStringCompleted; } //Start the Async call to find if NoGateway is true or false public void NoGatewayStatus() { //Start the Async's download //Do other work here wc.DownloadStringAsync(new Uri(www.xxxx.xxx)); } private void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { //Do work here } //Dispose of the NoGateway object public void Dispose() { wc.DownloadStringCompleted -= wc_DownloadStringCompleted; wc.Dispose(); GC.SuppressFinalize(this); } }
Question sur le code source:
-
Ici je n'ai pas ajouté le finaliseur, et normalement le finaliseur sera appelé par le GC, et le finaliseur fera appel à la Jeter. Comme je n'ai pas le finaliseur, quand dois-je appeler la méthode dispose? Est-ce le client de la classe qui a de l'appeler?
Donc ma classe dans l'exemple est appelée NoGateway et le client pourrait utiliser et de disposer de la classe comme ceci:
using(NoGateway objNoGateway = new NoGateway()) { //Do stuff here }
Serait la méthode dispose est automatiquement appelée lors de l'exécution atteint la fin de l'utilisation de bloc, ou le client manuellement l'appel de la méthode dispose? c'est à dire
NoGateway objNoGateway = new NoGateway(); //Do stuff with object objNoGateway.Dispose(); //finished with it
-
Je suis en utilisant le
WebClient
classe dans monNoGateway
classe. Parce queWebClient
met en œuvre laIDisposable
interface, est-ce à dire queWebClient
indirectement utilise des ressources non managées? Est-il une règle dure et rapide pour suivre ce? Comment puis-je savoir qu'une classe utilise des ressources non managées?
- est-ce compliqué modèle de conception réellement nécessaires pour résoudre cette ressource relase problème ?
Vous devez vous connecter pour publier un commentaire.
Recommandé IDisposable modèle est ici. Lors de la programmation d'une classe qui utilise IDisposable, en règle générale, vous devez utiliser deux modèles:
Lors de l'implémentation d'une classe scellée qui n'utilise pas les ressources non managées, il vous suffit de mettre en œuvre une méthode dispose, tout comme avec les implémentations d'interface:
Lors de la mise en œuvre d'un descellés classe, de faire comme ceci:
Avis que je n'ai pas déclaré un finaliseur dans
B
; vous devez seulement mettre en œuvre un finaliseur si vous avez de réelles ressources non managées d'en disposer. Le CLR traite finalisable objets différemment à la non-finalisable objets, même siSuppressFinalize
est appelé.Donc, vous ne devez pas déclarer un finaliseur, sauf si vous devez, mais vous donnez les héritiers de votre classe un crochet pour appeler votre
Dispose
et de mettre en œuvre un finaliseur s'ils utilisent des ressources non gérées directement:Si vous n'êtes pas en utilisant des ressources non gérées directement (
SafeHandle
et amis ne comptent pas, qu'ils le disent leurs propres outils de finalisation), alors ne pas mettre en œuvre un outil de finalisation, comme le gouvernement du canada traite finalisable classes différemment, même si plus tard vous souhaitez supprimer l'outil de finalisation. À noter également que, même siB
ne dispose pas d'un outil de finalisation, il appelle encoreSuppressFinalize
de traiter correctement toutes les sous-classes qui n'en œuvre d'un outil de finalisation.Lorsqu'une classe implémente l'interface IDisposable, cela signifie que quelque part il y a quelques ressources non managées, qui devrait être de s'en débarrasser lorsque vous avez fini d'utiliser la classe. Les ressources actuelles sont encapsulés dans les classes; vous ne devez pas explicitement les supprimer. Simplement en appelant
Dispose()
ou emballage de la classe dans unusing(...) {}
sera assurez-vous que toutes les ressources non managées sont de s'en débarrasser comme nécessaire.IDisposable
, les chances sont, il va traîner pendant un certain temps de toute façon. Vous êtes en train d'enregistrer le CLR l'effort d'avoir à les copier à partir de Gen0 -> Gen1 -> Gen2if (disposed) return;
?Officiel de motif de mettre en œuvre
IDisposable
est difficile à comprendre. Je crois que c'est mieux:Un encore mieux solution est d'avoir une règle qui vous toujours devez créer une classe wrapper pour toute ressource non managée que vous avez besoin pour gérer:
Avec
SafeHandle
et de ses dérivés, ces classes doivent être très rare.Le résultat jetables pour les classes qui ne traitent pas directement avec les ressources non managées, même en présence de l'héritage, est puissant: ils n'ont pas besoin d'être concerné par des ressources non managées plus. Ils seront simple à mettre en œuvre et à comprendre:
disposed
drapeau et de vérifier en conséquence.disposed
drapeau, les vérifier avant de les jeter et de les définir après l'élimination. Regardez ici pour l'idée. Vous devriez également vérifier le drapeau avant tout les méthodes de la classe. Du sens? Est-il compliqué?Noter que toute IDisposable mise en œuvre doit suivre le schéma ci-dessous (à mon humble avis). J'ai développé ce modèle basé sur des informations à partir de plusieurs excellents .NET des "dieux" de la .NET Cadre des lignes Directrices de Conception (notez que MSDN ne pas suivre ce pour quelque raison!). L' .NET Framework de Conception de lignes Directrices ont été rédigées par Krzysztof Cwalina (CLR Architecte à l'époque) et Brad Abrams (je crois que le CLR Gestionnaire de Programme à l'époque) et le projet de Loi Wagner ([Efficaces C#] et [Plus Efficace C#] (il suffit de prendre un coup d'oeil pour ces sur Amazon.com:
Noter que vous ne devriez JAMAIS mettre en œuvre un outil de finalisation, à moins que votre classe contient directement (pas en hérite) des ressources non managées. Une fois que vous mettre en œuvre un outil de finalisation dans une classe, même si elle n'est jamais appelée, c'est la garantie de vivre une collecte supplémentaire. Il est automatiquement placé dans la File d'attente de Finalisation (qui s'exécute sur un seul thread). Aussi, on remarque très importante...tout le code exécuté dans un Finaliseur (si vous avez besoin de mettre en œuvre un) DOIT être thread-safe ET d'exception-safe! Les MAUVAISES choses vont se passer autrement...(c'est à dire indéterminé comportement et, dans le cas d'une exception, une fatale irrécupérable plantage de l'application).
Le modèle que j'ai mis en place (et écrit un extrait de code pour) suit:
Voici le code pour la mise en œuvre de IDisposable dans une classe dérivée. Notez que vous ne devez pas explicitement la liste de l'héritage de IDisposable dans la définition de la classe dérivée.
J'ai posté cette application sur mon blog: Comment mettre Correctement en Œuvre le Modèle dispose
Interlocked.Exchange
sur un entierIsDisposed
drapeau dans le non-virtuel fonction wrapper serait plus sûr.Je suis d'accord avec pm100 (et doit avoir explicitement dit dans mon précédent post).
Vous ne devriez jamais mettre en œuvre IDisposable dans une classe, à moins que vous en avez besoin. Pour être très précis, il y a environ 5 fois quand vous avez besoin/doit mettre en œuvre IDisposable:
Votre classe contient explicitement (c'est à dire pas par héritage) toutes les ressources gérées de mettre en œuvre IDisposable et doit être nettoyé une fois que votre classe n'est plus utilisé. Par exemple, si votre classe contient une instance d'un Ruisseau, DbCommand, DataTable, etc.
Votre classe contient explicitement toutes les ressources gérées de mettre en œuvre une méthode Close () - par exemple, IDataReader, IDbConnection, etc. Notez que certaines de ces classes implémentent IDisposable en ayant Dispose() ainsi qu'une méthode Close ().
Votre classe contient explicitement une ressource non managée - par exemple, un objet COM, les pointeurs (oui, vous pouvez utiliser des pointeurs en C#, mais ils doivent être déclarés en "dangereux" blocs, etc.
Dans le cas de ressources non managées, vous devez également vous assurer de Système d'appel.Moment de l'exécution.InteropServices.Maréchal.ReleaseComObject() sur le BRF. Même si le BRF est, en théorie, un wrapper géré, il y a encore de comptage de référence passe sous les couvertures.
Si votre classe est abonnée à des événements à l'aide de solides références. Vous devez annuler l'inscription/détachez-vous de ces événements. Toujours assurez-vous que ce ne sont pas null avant d'essayer d'annuler l'inscription/détacher!.
Votre classe contient toute combinaison de ce qui précède...
Une alternative à travailler avec les objets COM et d'avoir à utiliser le Maréchal.ReleaseComObject() est à utiliser le Système.Moment de l'exécution.InteropServices.SafeHandle classe.
La BCL (Base Class Library de l'Équipe) a un bon billet de blog à ce sujet ici http://blogs.msdn.com/bclteam/archive/2005/03/16/396900.aspx
Une remarque très importante à faire est que si vous travaillez avec WCF et le nettoyage de ressources, vous devriez TOUJOURS éviter l'usage de bloc. Il y a beaucoup de messages de blog là-bas et certains sur MSDN sur pourquoi c'est une mauvaise idée. J'ai également posté ce sujet ici - N'utilisez pas de 'l'aide de()' avec un proxy WCF
À l'aide de lambda au lieu de IDisposable.
Je n'ai jamais été heureux avec l'ensemble à l'aide de/IDisposable idée. Le problème est qu'il nécessite l'appelant:
Ma nouvelle méthode préférée consiste à utiliser une méthode de fabrique et un lambda au lieu
Imaginez que je veux faire quelque chose avec une occurrence de SqlConnection (quelque chose qui devrait être enveloppé d'une aide). Classiquement, vous le feriez
Nouvelle façon
Dans le premier cas, l'appelant pouvait tout simplement pas utiliser l'aide de la syntaxe. DANS le second cas, l'utilisateur n'a pas le choix. Il n'existe pas de méthode qui crée un objet SqlConnection, l'appelant doit invoquer DoWithConnection.
DoWithConnection ressemble à ceci
MakeConnection
est maintenant en privéDoForAll(Action<T>) where T:IComparable<T>
, appelant le délégué a indiqué sur chaque enregistrement. Compte tenu de ces deux objets, qui renverra les données dans l'ordre de tri, comment la production de tous les éléments qui existent dans la collection, mais pas l'autre? Si les types de mise en œuvreIEnumerable<T>
, on pourrait effectuer une opération de fusion, mais qui ne fonctionnera pas avecDoForAll
.DoForAll
collections sans avoir à copier d'abord, dans son intégralité, dans une autre structure, serait d'utiliser deux fils, ce qui serait plutôt plus hoggish de ressources que d'utiliser simplement un couple IEnumerable et en faisant attention à leur libération.personne n'a répondu à la question de savoir si vous devez mettre en œuvre IDisposable même si vous n'avez pas besoin.
Réponse courte : Non
Réponse longue:
Cela permettrait aux consommateurs de votre classe à utiliser 'l'aide'. La question que je voudrais poser est - pourquoi auraient-ils le faire? La plupart des devs de ne pas utiliser "à l'aide" que si ils savent qu'ils doivent - et comment font-ils savoir. Soit
En œuvre IDisposable vous dites devs (au moins certains) que cette classe encapsule quelque chose qui doit être libéré. Ils vont l'utiliser "à l'aide" - mais il y a d'autres cas où l'aide n'est pas possible (la portée de l'objet n'est pas local); et ils devront commencer à se soucier de la durée de vie des objets dans les autres cas, - je m'inquiéter pour vous. Mais ce n'est pas nécessaire
Vous mettre en œuvre Idisposable de permettre l'utilisation de l'aide, mais ils l'habitude de l'utiliser à l'aide, sauf si vous le leur demandez.
Afin de ne pas le faire
Si vous utilisez d'autres objets gérés à l'aide de ressources non managées, il n'est pas de votre responsabilité de vous assurer que les personnes sont finalisés. Votre responsabilité est d'appeler dispose sur ces objets lorsque Disposer est appelée sur votre objet, et ça s'arrête là.
Si votre classe n'utilise pas toutes les ressources rares, je ne vois pas pourquoi vous voulez faire de votre classe en œuvre IDisposable. Vous devriez le faire que si vous êtes:
Oui, le code qui utilise votre code doit appeler la méthode dispose de l'objet. Et oui, le code qui utilise votre objet peut utiliser
using
comme vous l'avez indiqué.(2?) Il est probable que le client web utilise des ressources non managées, ou d'autres ressources que de mettre en œuvre IDisposable. La raison exacte, cependant, n'est pas important. Ce qui est important, c'est qu'il met en œuvre IDisposable, et il tombe sur vous pour agir en fonction de cette connaissance par l'élimination de l'objet lorsque vous avez terminé avec lui, même si il s'avère que le client web n'utilise pas d'autres ressources.
Modèle dispose:
Exemple d'héritage:
Certains aspects de une autre réponse sont légèrement incorrect pour 2 raisons:
D'abord,
est en fait équivalent à:
Cela peut sembler ridicule, depuis la "nouvelle" de l'opérateur ne doit jamais retourner 'null', sauf si vous avez une exception OutOfMemory. Mais considérons les cas suivants:
1. Vous appelez un FactoryClass qui renvoie un IDisposable de ressources ou
2. Si vous avez un type qui peut ou ne peut pas hériter de IDisposable en fonction de sa mise en œuvre - n'oubliez pas que j'ai vu la IDisposable modèle mis en œuvre de manière incorrecte à de nombreuses reprises à de nombreux clients où les développeurs il suffit d'ajouter une méthode dispose() sans hériter de IDisposable (mauvaise, mauvaise, mauvaise). Vous pourriez également avoir le cas d'une IDisposable ressource a été obtenu à partir d'une propriété ou d'une méthode (encore une fois mauvais, mauvais, mauvais - ne pas " donner votre IDisposable ressources)
Si le "comme" de l'opérateur renvoie la valeur null (ou de la propriété ou de la méthode de retour de la ressource), et de votre code dans le "utilise" bloc protège contre les 'null', votre code ne sera pas sauter lorsque vous essayez d'appeler Disposer sur un objet nul en raison de la "intégrée" null vérifier.
La deuxième raison, votre réponse n'est pas exact, c'est pour les raisons suivantes stmt:
D'abord, la Finalisation (ainsi que GC) est non-déterministe. Le CLR détermine le moment où il va appeler un finaliseur. c'est à dire le développeur/code n'a aucune idée. Si la IDisposable modèle est correctement mis en œuvre (comme je l'ai posté ci-dessus) et GC.SuppressFinalize() a été appelée, le Finaliseur ne sera PAS appelé. C'est l'une des raisons majeures à mettre en œuvre correctement le modèle correctement. Depuis il est à seulement 1 Finaliseur thread par processus de gestion, quel que soit le nombre de processeurs logiques, vous pouvez facilement dégrader les performances en sauvegardant ou même suspendus le Finaliseur thread par l'oubli de l'appeler GC.SuppressFinalize().
J'ai posté une mise en œuvre correcte du Modèle dispose sur mon blog: Comment mettre Correctement en Œuvre le Modèle dispose
NoGateway = new NoGateway();
etNoGateway != null
?1) WebClient est un type géré, de sorte que vous n'avez pas besoin d'un outil de finalisation. Le finalizer est nécessaire dans le cas où vos utilisateurs ne Dispose() de votre NoGateway classe et le type natif (qui n'est pas collectée par le GC) doit être nettoyé après. Dans ce cas, si l'utilisateur n'a pas d'appel Dispose(), le contenu de l'WebClient seront éliminés par le GC à droite après le NoGateway n'.
2) Indirectement oui, mais vous ne devriez pas avoir à vous en préoccuper. Votre code est correct que des stands et vous ne pouvez pas empêcher vos utilisateurs de l'oubli Dispose() très facilement.
Modèle à partir de msdn
est équivalent à
Un finaliseur est appelé lors de la GC de détruire votre objet. Cela peut être à un moment totalement différent que lorsque vous quittez votre méthode. La dispose de IDisposable est appelée immédiatement après que vous quittez l'aide du bloc. D'où le motif est généralement utiliser l'aide pour libérer des ressources immédiatement après, vous n'avez pas besoin de plus.
De ce que je sais, il est fortement recommandé de ne PAS utiliser l'outil de finalisation /Destructeur:
Plupart du temps, cela est dû à ne pas savoir quand il va être appelé. La méthode dispose est beaucoup mieux, surtout si vous nous utiliser ou de disposer directement.
l'aide est bonne. l'utiliser 🙂