2009-06-30 7 views
142

J'envoie un flux à des méthodes d'écriture, et dans ces méthodes j'utilise un lecteur binaire/wrtier. Quand le lecteur/écrivain est disposé, soit par using ou juste quand il n'est pas référencé, le flux est-il fermé aussi? Je voudrais envoyer un BinaryReader/Writer, mais j'utilise aussi un StreamReader (peut-être que je devrais contourner cela, je ne l'utilise que pour GetLine et ReadLine). Ceci est très gênant s'il ferme le flux chaque fois qu'un écrivain/lecteur se ferme.Est-ce que streamreader dispose du flux?

Répondre

166

Oui, StreamReader, StreamWriter, BinaryReader et BinaryWriter tout près/éliminer leurs flux sous-jacents lorsque vous appelez Dispose sur eux. Ils ne pas disposer du flux si le lecteur/écrivain est juste garbage collection bien - vous devez toujours disposer du lecteur/écrivain, de préférence avec une déclaration using. (En fait, aucune de ces classes n'a de finaliseurs, et ne devrait pas l'être.)

Personnellement, je préfère avoir une instruction using pour le flux. Vous pouvez imbriquer using déclarations sans accolades assez nettement:

using (Stream stream = ...) 
using (StreamReader reader = new StreamReader(stream, Encoding.Whatever)) 
{ 
} 

Même si la déclaration using pour le flux est un peu redondant (à moins que le constructeur StreamReader lance une exception) je considère qu'il est plus pratique alors si vous vous débarrasser de la StreamReader et juste utiliser le flux directement à une date ultérieure, vous aurez déjà la bonne sémantique d'élimination.

+2

Ah bon, il ne se produit que lorsque vous appelez Dispose, pas lors de la finalisation soi-disant. – Nefzen

+1

@Nefzen: C'est parce qu'il n'y a aucune garantie quel ordre vos objets seront finalisés. Si le StreamReader et le Stream sous-jacent sont tous deux éligibles pour la finalisation, le GC peut finaliser le flux en premier - alors streamreader n'aurait pas de référence à stream. Pour cette raison, vous pouvez uniquement libérer des ressources non managées à l'intérieur d'une finalisation (par exemple, un FileStream ferme son handle de fichier Windows dans sa finalisation). Oh, et bien sûr, si vous ne disposez jamais, le flux sera toujours collecté (et le fichier fermé). C'est juste une très mauvaise pratique de ne pas disposer un flux. – JMarsch

+9

Cette imbrication fait que l'analyseur de code VS se plaint: 'CA2202: Microsoft.Usage: L'objet 'stream' peut être éliminé plusieurs fois dans la méthode '...'. Pour éviter de générer une exception System.ObjectDisposedException, vous ne devez pas appeler Dispose plus d'une fois sur un objet. »Cela devrait-il être simplement ignoré? Je n'ai obtenu aucune exception jusqu'à présent ... –

-3

le flux disposé soit par « en utilisant » mot-clé ou d'appeler explicitement disposer

27

Oui, il le fait. Vous pouvez vérifier cela en regardant l'implémentation avec Reflector.

protected override void Dispose(bool disposing) 
{ 
    try 
    { 
     if ((this.Closable && disposing) && (this.stream != null)) 
     { 
      this.stream.Close(); 
     } 
    } 
    finally 
    { 
     if (this.Closable && (this.stream != null)) 
     {  
      this.stream = null;  
      this.encoding = null; 
      this.decoder = null; 
      this.byteBuffer = null; 
      this.charBuffer = null; 
      this.charPos = 0; 
      this.charLen = 0; 
      base.Dispose(disposing); 
     } 
    } 
} 
2

Oui. Appeler Dispose() sur et IDisposable (que "using" fait) devrait faire en sorte que l'objet nettoie toutes ses ressources. Cela inclut les flux vidant et fermant leurs descripteurs de fichier.

Si, dans votre cas, vous souhaitez le passer à d'autres méthodes, vous devez vous assurer que ces méthodes ne font pas leur lecture/écriture dans un bloc using.

31

C'est un ancien, mais je voulais faire quelque chose aujourd'hui semblable et a constaté que les choses ont changé. Depuis .net 4.5, il y a un argument leaveOpen:

public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool leaveOpen) 

Le seul problème est qu'il est pas tout à fait évident que pour définir les autres paramètres. Voici un peu d'aide:

De the msdn page pour StreamReader Constructor (flux):

Ce constructeur initialise le codage UTF8Encoding, la propriété de BaseStream en utilisant le paramètre de flux, et la taille du tampon interne 1024 octets.

Cela laisse simplement detectEncodingFromByteOrderMarks qui à en juger par the source code est true

public StreamReader(Stream stream) 
     : this(stream, true) { 
} 

public StreamReader(Stream stream, bool detectEncodingFromByteOrderMarks) 
     : this(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize) { 
} 

Ce serait bien si certains de ces défauts ont été exposés ou si les arguments étaient facultatifs afin que nous puissions simplement préciser les que nous voulons.

+0

Très belle info! Je n'ai jamais entendu parler de ce nouveau paramètre et cela a vraiment beaucoup de sens. – julealgon

+0

N'a pas remarqué la nouvelle addition en 4.5, merci! – TimothyP

8

Six ans de retard, mais peut-être que cela pourrait aider quelqu'un. StreamReader ferme la connexion quand elle est éliminée.

Cependant, "l'utilisation de (Stream stream = ...) {...}" avec StreamReader/StreamWriter peut entraîner la suppression du Stream deux fois: (1) lorsque l'objet StreamReader est éliminé (2) et lorsque le Stream utilise un bloc se ferme. Cela entraîne un avertissement CA2202 lors de l'exécution de l'analyse de code de VS.

Une autre solution, tirée directement de la page CA2202, consiste à utiliser un bloc try/finally. Installation correctement, ceci fermera seulement la connexion une fois.

Près du bas de CA2202, Microsoft recommande d'utiliser les éléments suivants:

Stream stream = null; 
try 
{ 
    stream = new FileStream("file.txt", FileMode.OpenOrCreate); 
    using (StreamWriter writer = new StreamWriter(stream)) 
    { 
     stream = null; 
     // Use the writer object... 
    } 
} 
finally 
{ 
    if(stream != null) 
     stream.Dispose(); 
} 

au lieu de ...

// Generates a CA2202 warning 
using (Stream stream = new FileStream("file.txt", FileMode.Open)) 
using (XmlReader reader = new XmlReader (stream)) 
{ 
    // Use the reader object... 
} 
+2

Oui, cela m'a été très utile, maintenant 7 ans plus tard :) – Sebastian

+0

L'avertissement est aussi discuté dans les commentaires de la réponse acceptée. Jon Skeet offre quelques conseils là-bas. – Marcin

Questions connexes