La suppression de streamreader ferme-t-elle le flux?

166

J'envoie un flux aux méthodes sur lesquelles écrire, et dans ces méthodes, j'utilise un lecteur / rédacteur binaire. Lorsque le lecteur / écrivain est éliminé, soit par usingou juste quand il n'est pas référencé, le flux est-il également fermé?

J'enverrais 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 assez gênant s'il ferme le flux chaque fois qu'un écrivain / lecteur est fermé.

Nefzen
la source

Réponses:

204

Oui, StreamReader, StreamWriter, BinaryReaderet BinaryWritertout près / éliminer leurs flux sous - jacents lorsque vous appelez Disposesur eux. Cependant, ils ne suppriment pas le flux si le lecteur / écrivain est simplement ramassé - vous devez toujours vous débarrasser du lecteur / écrivain, de préférence avec une usinginstruction. (En fait, aucune de ces classes n'a de finaliseur, ni ne devrait en avoir.)

Personnellement, je préfère également avoir une instruction using pour le flux. Vous pouvez usingparfaitement imbriquer des instructions sans accolades:

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

Même si l' usinginstruction pour le flux est quelque peu redondante (à moins que le StreamReaderconstructeur ne lève une exception), je considère que c'est la meilleure pratique, car si vous vous débarrassez de StreamReaderet utilisez simplement le flux directement à une date ultérieure, vous aurez déjà la bonne disposition sémantique.

Jon Skeet
la source
2
oh bien, cela n'arrive que lors de l'appel de Dispose, pas lors de la finalisation supposée.
Nefzen
1
@Nefzen: C'est parce qu'il n'y a aucune garantie dans quel ordre vos objets seront finalisés. Si le StreamReader et le Stream sous-jacent sont tous deux éligibles pour la finalisation, le GC pourrait finaliser le flux en premier - alors streamreader n'aurait pas de référence au flux. Pour cette raison, vous ne pouvez libérer des ressources non gérées qu'à l'intérieur d'une finalisation (par exemple, un FileStream ferme son descripteur de fichier Windows lors de sa finalisation). Oh, et bien sûr, si vous ne supprimez jamais, le flux sera toujours collecté par la suite (et le fichier fermé). C'est juste une très mauvaise pratique de ne pas supprimer un flux.
JMarsch
13
Cette imbrication amène l'analyseur de code VS à se plaindre: cela CA2202 : Microsoft.Usage : Object 'stream' can be disposed more than once in method '...'. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object.devrait-il simplement être ignoré? Je n'ai eu aucune exception jusqu'à présent ...
HB
15
@HB: Il est prudent de l'ignorer dans ce cas. Ou vous pouvez simplement créer le flux dans l'appel du constructeur à StreamReader. L'avertissement me semble faux, étant donné que la documentation relative à l'indication précise IDisposable.Dispose: "Si la méthode Dispose d'un objet est appelée plus d'une fois, l'objet doit ignorer tous les appels après le premier. L'objet ne doit pas lever d'exception si sa méthode Dispose est appelé plusieurs fois. "
Jon Skeet
5
@JonSkeet: En fait, il y a une page pour ça , vous aviez raison, c'est faux :)
HB
45

C'est un ancien, mais je voulais faire quelque chose de similaire aujourd'hui et j'ai constaté que les choses avaient changé. Depuis .net 4.5, il y a un leaveOpenargument:

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

Le seul problème est qu'il n'est pas tout à fait évident de définir ce qu'il faut définir pour les autres paramètres. Voici de l'aide:

À partir de la page msdn pour StreamReader Constructor (Stream):

Ce constructeur initialise le codage à UTF8Encoding, la propriété BaseStream à l'aide du paramètre stream et la taille de la mémoire tampon interne à 1024 octets.

Cela laisse juste detectEncodingFromByteOrderMarksce qui à en juger par le code source esttrue

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

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

Ce serait bien si certaines de ces valeurs par défaut étaient exposées ou si les arguments étaient facultatifs afin que nous puissions simplement spécifier ceux que nous voulons.

acarlon
la source
Très belle info! Jamais entendu parler de ce nouveau paramètre et cela a en fait beaucoup de sens.
julealgon
3
Pour les paresseux comme moi, la réponse courte pour laisser le flux ouvert serait la suivante:using (var streamReader = new StreamReader(myStream, Encoding.UTF8, true, 1024, true))
beawolf
29

Oui. Vous pouvez le vérifier 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);
        }
    }
}
Brian Rasmussen
la source
13

Six ans de retard mais peut-être que cela pourrait aider quelqu'un.

StreamReader ferme la connexion lorsqu'elle est supprimée. Cependant, "using (Stream stream = ...) {...}" avec StreamReader / StreamWriter peut entraîner la suppression du Stream deux fois: (1) lorsque l'objet StreamReader est supprimé (2) et lorsque le bloc Stream using 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. Configuration correctement, cela ne fermera la connexion qu'une seule 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...
}
Sunsetquest
la source
2
L'avertissement est également discuté dans les commentaires de la réponse acceptée. Jon Skeet y donne quelques conseils.
Marcin
Sachez également que l'instruction using est en fait convertie en un bloc try-finally par le compilateur.
Jason Kelley
2

Oui. L'appel de Dispose () on et IDisposable (ce que fait "using") devrait permettre à un objet de nettoyer toutes ses ressources. Cela inclut le vidage des flux et la fermeture de leurs descripteurs de fichiers.

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.

Joe M
la source
-2

le flux éliminé soit en "utilisant" le mot-clé ou en appelant explicitement dispose

Ahmed Said
la source