2010-11-29 48 views
3

J'ai une classe qui lit les données d'un flux de fichier et écrit dans un autre. Je suis préoccupé par la fermeture des flux après la fin du traitement dans closeFiles().Fermeture d'un fichier sans utiliser l'aide

Comment gérez-vous la possibilité que l'élimination d'un flux puisse déclencher une exception empêchant l'élimination de l'autre flux d'être appelé?

Dois-je appeler à proximité et disposer sur les flux ou juste un? Que se passe-t-il si j'attrape des erreurs du flux et que je continue avec le déplacement et la suppression des fichiers comme indiqué dans lastOperation()?

Dans un monde parfait, j'aimerais utiliser une instruction using dans une liste d'initialisation de style C++, mais je suis à peu près sûr que ce n'est pas possible dans C#.

EDIT: merci pour les réponses rapides les gars. Donc ce que je devrais faire est de tirer IDisposable puis modifiez le constructeur et ajouter les deux méthodes comme celui-ci ?: disposant

~FileProcessor() 
    { 
     Dispose(true); 
    } 

    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 
    private void Dispose(bool disposing) 
    { 
     if (!this.disposed) 
     { 
      if (disposing) 
      { 
       sw.Flush(); 
      } 
      closeFiles(); 
      disposed = true; 
     } 
    } 

C'est essentiellement ce que je fais:

class FileProcessor 
{ 
    private string in_filename; 
    private string out_filename; 
    private StreamReader sr; 
    private StreamWriter sw; 
    bool filesOpen = false; 

    public FileProcessor(string filename) 
    { 
     in_filename = filename; 
     out_filename = filename + ".out"; 
     openFiles(); 
    } 

    ~FileProcessor() 
    { 
     closeFiles(); 
    } 

    private void openFiles() 
    { 
     sr = new StreamReader(in_filename); 
     sw = new StreamWriter(out_filename); 
     filesOpen = true; 
    } 

    private void closeFiles() 
    { 
     if (filesOpen) 
     { 
      sr.Close(); 
      sw.Close(); 
      sr.Dispose(); 
      sw.Dispose(); 
      filesOpen = false; 
     } 
    } 

    /* various functions to read, process and write to the files */ 

    public void lastOperation() 
    { 
     closeFiles(); 
     File.Delete(in_filename); 
     Directory.Move(out_filename, outdir + out_filename); 
    } 
} 
+0

Re the Edit: Vous devriez toujours perdre le destructeur. Et vérifiez sr, sw individuellement. –

+0

Merci Henk, j'ai vu votre addition sur la vérification individuelle après avoir fait l'édition, c'est déjà dans mon code :).J'ai obtenu le 'conseil' pour garder le destructeur de http://msdn.microsoft.com/en-us/library/system.idisposable.dispose(VS.71).aspx qui suggère "Employer la syntaxe de destructeur de C# pour le code de finalisation. Ce destructeur ne fonctionnera que si la méthode Dispose n'est pas appelée. " Je lis ceci comme le destructeur ne sera pas cher si l'objet est éliminé mais qu'il y a des circonstances où le destructeur sera nécessaire ... Je pourrais commencer une autre question à ce sujet spécifiquement. – Patrick

+0

Patrick, la classe dans cet exemple MSDN a une ressource non gérée. Vous avez seulement des ressources gérées. –

Répondre

3

Votre classe FileProcessor ne doit pas avoir de destructeur. Cela ne sert à rien mais c'est cher.

Il doit avoir un Dispose() (et implémenter l'interface IDisposable) pour appeler closeFiles().

Et comme @marcelo a répondu, Stream.Dispose() ne devrait pas jeter. Vous pouvez compter sur cela pour les classes BCL.

Mais vous devriez vérifier chaque lecteur/enregistreur pour null, dans le cas où le premier ouvert, mais le second a échoué:

if (sr != null) sr.Dispose(); 
if (sw != null) sw.Dispose(); 

Votre filesOpen ne peut pas couvrir à la fois.

2

méthodes Dispose ne devrait jamais jeter des exceptions. Il y a même un outil d'analyse de code warning pour cela.

+0

Les méthodes Dispose doivent lancer des exceptions si et seulement si elles ne peuvent pas satisfaire les post-conditions attendues en ce qui concerne les objets affectés par l'objet à éliminer. Éliminer les méthodes qui échouent sont mauvaises, mais éliminer les méthodes qui échouent silencieusement est pire. – supercat

+0

@supercact: Dans mon expérience, les choses qui échouent à leur sortie ne font qu'empirer les choses en lançant des exceptions. La meilleure politique que j'ai trouvée consiste à enregistrer une erreur, à la signaler, etc., puis à poursuivre le déroulement. Peut-être que je suis en désaccord avec les normes de l'industrie ici. Je suis plutôt un gars C++, et en C++, lancer une exception d'un destructeur est fondamentalement anathème. Le conseil est-il différent pour C#? Avez-vous un lien que je peux parcourir? –

+0

Qualificateur: Je comprends que Dispose n'est pas un destructeur, mais pour moi le principe de fonctionnement est à peu près le même, et donc les risques de lancer une exception dans Dispose sont à peu près les mêmes que pour le destructeur. Peut-être que je ne suis pas d'accord là-dessus, mais encore une fois, il serait bon de savoir quelle est la norme de l'industrie. –

0

En C#, il existe using. L'objet fourni à l'instruction using doit implémenter l'interface IDisposable. Cette interface fournit la méthode Dispose, qui doit libérer les ressources de l'objet.

Si votre StreamReader et StreamWriter implémentent IDisposable, vous pouvez les placer dans un bloc using, et ils seront éliminés proprement lorsque vous en aurez fini avec eux.

using(var sr = new StreamReader(in_filename)) { 
    // Perform reader actions 
} 
// Reader will now be disposed. 
+0

Mais j'ouvre le flux dans le constructeur et je dois le garder ouvert pendant toute la durée de la classe ... – Patrick

+0

Ensuite, placez la classe dans un bloc using. – Greg

+2

Mettez la classe dans un bloc using? C'est un conseil erroné, car il ne peut être fait. Comment voulez-vous que ce genre de code fonctionne? –

3

Je pense qu'il est une bonne pratique d'avoir votre classe implémente l'interface IDisposable si vous utilisez l'objet IDisposable à l'intérieur. Ensuite, vous devez vous assurer que, dans votre implémentation Dispose(), ne lancez pas d'exceptions. Si chaque objet que vous disposez fait cette garantie, votre client sera en sécurité.