Dispose () ou Finalize () doivent-ils être utilisés pour supprimer des fichiers temporaires?

J’ai une classe qui utilise des fichiers temporaires ( Path.GetTempFileName() ) tant qu’elle est active. Je veux m’assurer que ces fichiers ne restnt pas sur le disque dur de l’utilisateur, ce qui prend de l’espace après la fermeture de mon programme. À l’heure actuelle, ma classe a une méthode Close() qui vérifie si des fichiers temporaires utilisés par la classe existent toujours et les supprime.

Serait-il plus logique de placer ce code dans les méthodes Dispose () ou Finalize () à la place?

Mieux encore serait de créer le fichier avec FileOptions.DeleteOnClose. Cela garantira que le système d’exploitation supprime de force le fichier lorsque votre processus se ferme (même dans le cas d’un abandon brutal). Bien sûr, vous voudrez quand même fermer / supprimer le fichier vous-même lorsque vous en aurez fini, mais cela constitue un bon backstop pour vous assurer que vous n’autoriserez pas les fichiers à restr inoubliable.

Je ferais les deux; Rendez la classe jetable et faites-la nettoyer par le finaliseur. Il existe un modèle standard pour le faire de manière sûre et efficace: utilisez-le plutôt que d’essayer de déduire vous-même quel est le bon modèle. Il est très facile de se tromper. Lisez ceci attentivement :

http://msdn.microsoft.com/en-us/library/system.idisposable.aspx

Notez que vous devez être vraiment très prudent lorsque vous écrivez un finaliseur. Lorsque le finaliseur s’exécute, bon nombre de vos hypothèses normales sont erronées:

  • Il existe toutes sortes de potentiels pour les conditions de concurrence ou les blocages car vous n’êtes plus sur le fil principal, vous êtes sur le fil de finalisation.

  • Dans le code standard, si vous exécutez du code dans un object, vous savez que tout ce à quoi l’object fait référence est actif. Dans un finaliseur, toutes les choses auxquelles l’object fait référence pourraient être juste finalisées! Les finaliseurs d’objects morts peuvent être exécutés dans n’importe quel ordre, y compris les objects “enfants” en cours de finalisation avant les objects “parents”.

  • Dans le code standard, l’atsortingbution d’une référence à un object à un champ statique peut être parfaitement judicieuse. Dans un finaliseur, la référence que vous affectez peut concerner un object déjà mort et, par conséquent, l’affectation ramène un object mort à la vie. (Parce que les objects référencés par des champs statiques sont toujours vivants.) C’est un état extrêmement étrange et rien d’agréable ne se produit si vous le faites.

  • Etc. Faites attention. Si vous écrivez un finaliseur non sortingvial, vous êtes censé comprendre parfaitement le fonctionnement du ramasse-miettes.

Un fichier est une ressource non gérée et vous implémentez IDisposable pour nettoyer les ressources non gérées dont dépendent vos classes.

J’ai implémenté des classes similaires, bien que jamais dans le code de production.

Toutefois, je comprends votre ténacité à ce sujet: une interaction de l’utilisateur avec les fichiers hors de votre application risquerait de tout gâcher et de poser des problèmes lors de leur élimination. Cependant, il en va de même pour tout fichier créé / supprimé par une application, qu’il soit ou non nettoyé par une méthode Dispose () ou non.

Je dois dire que la mise en œuvre d’IDisposable serait un choix raisonnable.

David M. Kean propose une méthode intéressante dans l’entrée MSDN de Path.GetTempFileName . Il crée une classe wrapper implémentant IDisposable qui supprimera automatiquement le fichier:

 public class TemporaryFile : IDisposable { private bool _isDisposed; public bool Keep { get; set; } public ssortingng Path { get; private set; } public TemporaryFile() : this(false) { } public TemporaryFile(bool shortLived) { this.Path = CreateTemporaryFile(shortLived); } ~TemporaryFile() { Dispose(false); } public void Dispose() { Dispose(false); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!_isDisposed) { _isDisposed = true; if (!this.Keep) { TryDelete(); } } } private void TryDelete() { try { File.Delete(this.Path); } catch (IOException) { } catch (UnauthorizedAccessException) { } } public static ssortingng CreateTemporaryFile(bool shortLived) { ssortingng temporaryFile = System.IO.Path.GetTempFileName(); if (shortLived) { // Set the temporary atsortingbute, meaning the file will live // in memory and will not be written to disk // File.SetAtsortingbutes(temporaryFile, File.GetAtsortingbutes(temporaryFile) | FileAtsortingbutes.Temporary); } return temporaryFile; } } 

Utiliser la nouvelle classe est facile, il suffit de taper ce qui suit:

 using (TemporaryFile temporaryFile = new TemporaryFile()) { // Use temporary file } 

Si vous décidez, après avoir construit un TemporaryFile, d’empêcher sa suppression, définissez simplement la propriété TemporaryFile.Keep sur true:

 using (TemporaryFile temporaryFile = new TemporaryFile()) { temporaryFile.Keep = true; } 

Je fais toujours mes classes qui pointent vers des fichiers temporaires IDisposable , et implémente généralement un finaliseur qui appelle là aussi ma méthode de disposition. Cela semble être le paradigme suggéré par la page MSDN IDisposable .

Code associé ci-dessous:

 public void Dispose() { Dispose(true); // This object will be cleaned up by the Dispose method. // Therefore, you should call GC.SupressFinalize to // take this object off the finalization queue // and prevent finalization code for this object // from executing a second time. GC.SuppressFinalize(this); } // Dispose(bool disposing) executes in two distinct scenarios. // If disposing equals true, the method has been called directly // or indirectly by a user's code. Managed and unmanaged resources // can be disposed. // If disposing equals false, the method has been called by the // runtime from inside the finalizer and you should not reference // other objects. Only unmanaged resources can be disposed. private void Dispose(bool disposing) { // Check to see if Dispose has already been called. if(!this.disposed) { // If disposing equals true, dispose all managed // and unmanaged resources. if(disposing) { // Dispose managed resources. } // Call the appropriate methods to clean up // unmanaged resources here. // If disposing is false, // only the following code is executed. // Note disposing has been done. disposed = true; } } // Use C# destructor syntax for finalization code. // This destructor will run only if the Dispose method // does not get called. // It gives your base class the opportunity to finalize. // Do not provide destructors in types derived from this class. ~MyResource() { // Do not re-create Dispose clean-up code here. // Calling Dispose(false) is optimal in terms of // readability and maintainability. Dispose(false); } 

Si vous souhaitez réutiliser vos fichiers temporaires, par exemple, ouvrez \ close \ read \ write \ etc, les effacer au niveau de déchargement AppDomain peut être utile.

Cela peut être utilisé en combinaison avec le placement de fichiers temporaires dans un sous-répertoire bien connu d’un emplacement temporaire et en s’assurant que le répertoire est supprimé au démarrage de l’application pour garantir que les arrêts non nettoyés sont pris en charge.

Un exemple de base de la technique (avec la gestion des exceptions supprimée autour de supprimer pour la brièveté). J’utilise cette technique dans les tests unitaires basés sur des fichiers où cela a du sens et est utile.

 public static class TempFileManager { private static readonly List TempFiles = new List(); private static readonly object SyncObj = new object(); static TempFileManager() { AppDomain.CurrentDomain.DomainUnload += CurrentDomainDomainUnload; } private static void CurrentDomainDomainUnload(object sender, EventArgs e) { TempFiles.FindAll(file => File.Exists(file.FullName)).ForEach(file => file.Delete()); } public static FileInfo CreateTempFile(bool autoDelete) { FileInfo tempFile = new FileInfo(Path.GetTempFileName()); if (autoDelete) { lock (SyncObj) { TempFiles.Add(tempFile); } } return tempFile; } } 

Absolument. De cette façon, vous pouvez assurer le nettoyage avec les exceptions présentes.

Vous devez absolument utiliser Dispose pour nettoyer les ressources, mais veillez à implémenter l’interface IDisposable . Vous ne voulez pas simplement append une méthode nommée Dispose .