Quand disposer et pourquoi?

J’ai posé une question sur cette méthode:

// Save an object out to the disk public static void SerializeObject(this T toSerialize, Ssortingng filename) { XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType()); TextWriter textWriter = new StreamWriter(filename); xmlSerializer.Serialize(textWriter, toSerialize); textWriter.Close(); } 

dans la réponse, j’ai eu ceci comme remarque ajoutée:

Assurez-vous de toujours disposer des ressources disponibles telles que les stream, les lecteurs de texte et les rédacteurs. Cela ne semble pas être le cas dans votre méthode SerializeObject.

Donc, je peux dire que cela va paraître super boiteux pour quelqu’un qui code C # depuis un an ou deux, mais pourquoi dois-je en disposer?

Est-ce que testWriter a une méthode de disposition, mais la récupération de place ne devrait-elle pas s’en occuper? Je suis venu de Delphi en C #. À Delphes, je devais tout nettoyer, je ne voulais donc pas être paresseux. On vient de me dire que si vous forcez la libération de la mémoire de vos objects, cela peut causer des problèmes. On m’a dit de «laisser le ramasse-miettes le faire».

  1. Alors, pourquoi dois-je téléphoner à Dispose? (Je suppose que c’est parce que textWriter frappe le disque.)
  2. Y a-t-il une liste d’objects avec lesquels je dois faire attention? (Ou un moyen facile de savoir quand j’ai besoin d’appeler disposer?)

Vous avez raison de dire que pour le code correctement écrit, le GC va éventuellement nettoyer les ressources natives. L’object disposera d’un finaliseur et, lors de la finalisation, libérera les ressources natives nécessaires.

Cependant, lorsque cela se produit, cela n’est pas très déterministe. De plus, c’est un peu en arrière parce que vous utilisez le GC qui a été conçu pour gérer la mémoire gérée comme un moyen de gérer les ressources natives. Cela conduit à des cas intéressants et peut amener les ressources natives à restr en vie beaucoup plus longtemps que prévu, ce qui peut conduire à des situations où

  • Les fichiers sont ouverts longtemps après qu’ils ne sont plus utilisés
  • Les ressources peuvent être épuisées car le CPG ne voit pas suffisamment de mémoire pour forcer une collecte et par conséquent exécuter les finaliseurs

Le modèle using / dispose ajoute un déterminisme au nettoyage des ressources natives et supprime ces problèmes.

La règle de base est assez simple: appelez toujours Dispose() sur les objects qui implémentent IDisposable (tous les objects ne le font pas). Vous ne saurez pas toujours la raison pour laquelle un object doit implémenter Dispose, mais vous devez supposer qu’il existe pour une raison.

Le moyen le plus simple de vous assurer de le faire consiste à using :

 using (TextWriter tw = new StreamWriter(fileName)) { // your code here } 

Ceci appellera automatiquement Dispose() à la fin du bloc using (c’est fondamentalement la même chose que d’utiliser try / catch / finally avec Dispose () dans le bloc finally).

Pour plus d’informations sur le fonctionnement de Dispose avec la récupération de place, cliquez ici .

Le ramasse-miettes libère toutes les ressources, mais l’heure à laquelle il le fait n’est pas définie. La méthode Dispose offre un moyen de libérer immédiatement les ressources non gérées.

Si vous savez que vous n’allez pas utiliser une ressource donnée, vous pouvez simplement en disposer vous-même. vous serez certainement plus rapide que le ramasse-miettes et autoriserez les autres utilisateurs à utiliser le fichier ou tout ce que vous avez ouvert plus rapidement. Le moyen le plus simple serait d’utiliser votre TextWriter ou toute autre ressource de manière:

 using (TextWriter textWriter = new StreamWriter(filename)) { xmlSerializer.Serialize(textWriter, toSerialize); } 

Cela garantit essentiellement que TextWriter est éliminé à la fin. De toute façon, vous n’en avez pas besoin plus que ça.

En fait, vous l’avez déjà depuis que la méthode textWriter.Close le fait.

 public virtual void Close() { this.Dispose(true); GC.SuppressFinalize(this); } 

Vous pouvez donc changer votre code en. Ce

 public static void SerializeObject(this T toSerialize, Ssortingng filename) { TextWriter textWriter; try { XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType()); textWriter = new StreamWriter(filename); xmlSerializer.Serialize(textWriter, toSerialize); } finally { textWriter.Close(); } 

Ce qui est assez similaire à ce que using () fait dans les autres réponses.

Si vous ne le faites pas, une erreur avec Serialize risque de se produire avant que Framework ne lâche son verrou de fichier (lorsqu’il traite la queue fReachable).

Je sais que FxCop vous indique quand implémenter IDisposable, mais je ne pense pas qu’il soit facile de savoir quand vous devez appeler Dispose autrement qu’en consultant les documents et en vérifiant si un object implémente IDisposable (ou intellisense).

Si vous utilisez des ressources natives (par exemple, des descripteurs de fichier), vous devez appeler Dispose () pour les fermer bientôt, et non lorsque le GC est exécuté (ce qui pourrait être beaucoup plus tard dans les générations supérieures de gc). Et vous voulez fermer le fichier car l’access au fichier verrouille généralement le fichier d’une manière ou d’une autre.

Si vous ouvrez une ressource (telle qu’un fichier ou ouvrez une connexion à une firebase database), alors la disposition de la ressource libérera sa retenue sur la ressource. Si vous ne le faites pas, d’autres personnes pourraient ne pas être en mesure de se connecter à la firebase database ou d’utiliser le fichier.

En règle générale, si la classe implémente l’interface IDisposable, vous devez appeler la méthode Dispose () lorsque vous la terminez. Plus que probablement il y avait une raison pour les rendre jetables 🙂

Dans la documentation TextWriter.Dispose :

Remarque: Toujours appeler Dispose avant de libérer votre dernière référence à TextWriter. Sinon, les ressources qu’il utilise ne seront pas libérées tant que le garbage collector n’aura pas appelé la méthode Finalize de l’object TextWriter.

Dans la documentation Object.Finalize :

L’heure exacte à laquelle le finaliseur s’exécute pendant le nettoyage de la mémoire n’est pas définie. La libération des ressources n’est pas garantie à un moment donné, à moins d’appeler une méthode Close ou une méthode Dispose.

et

La méthode Finalize risque de ne pas aboutir ou de ne pas fonctionner du tout dans les circonstances exceptionnelles suivantes:

  • Un autre finaliseur bloque indéfiniment (passe dans une boucle infinie, essaie d’obtenir un verrou qu’il ne peut jamais obtenir, etc.). Étant donné que le moteur d’exécution tente d’exécuter les finaliseurs jusqu’à son terme, d’autres finaliseurs ne peuvent pas être appelés si un finaliseur bloque indéfiniment.

  • Le processus se termine sans laisser au runtime l’occasion de nettoyer. Dans ce cas, la première notification du moteur d’exécution de la fin du processus est une notification DLL_PROCESS_DETACH.

Le moteur d’exécution continue de finaliser les objects lors de l’arrêt uniquement alors que le nombre d’objects finalisables continue de diminuer.

Le ramasse-miettes va en effet “s’occuper de ça”. Tôt ou tard. Quand il arrive d’appeler le finaliseur, après le prochain ramassage des ordures.

Calling Dispose garantit que les ressources (telles que les descripteurs de fichier) sont libérées dès que possible, ce qui les rend disponibles pour une utilisation ultérieure par d’autres processus du système. La plupart du temps, vous ne remarquerez pas la différence entre appeler Dispose vous-même et laisser le ramasse-miettes le gérer. Mais il y a des moments où vous voulez. Un robot Web, par exemple, qui crée de nombreux objects HttpWebResponse , s’épuisera rapidement en connexions si vous ne les disposez pas après leur utilisation. (Non pas que j’ai rencontré ce problème … non, pas moi.)

La raison pour appeler Dispose plutôt que d’attendre le GC est souvent due au fait que vous utilisez une ressource système finie que vous souhaitez libérer le plus rapidement possible afin que d’autres processus ou unités d’exécution puissent l’utiliser. Pour les objects ayant le motif IDisposable, la construction “using” est un moyen facile et lisible de s’assurer que Dispose est appelé.

En général, vous devez disposer des objects lorsque vous n’en avez plus besoin.

Ne pas supprimer d’object lorsque vous en avez fini avec eux sera un sens qui bloquera l’access à d’autres applications traitées / traitées.

Un programmeur qui pense que l’on n’a pas à s’inquiéter de disposer d’éléments, car un finaliseur s’en occupera, c’est comme d’un conducteur qui pense qu’on n’a pas à s’inquiéter d’éviter des collisions car la voiture est équipée d’un airbag. Oui, un airbag rendra les choses plus faciles à survivre, mais …