web-dev-qa-db-fra.com

utilisant l'instruction FileStream et/ou StreamReader - Avertissements de Visual Studio 2012

Le nouveau Visual Studio 2012 se plaint d'une combinaison de codes commune que j'ai toujours utilisée. Je sais que cela semble exagéré, mais j’ai fait ce qui suit dans mon code «juste pour être sûr».

using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
    using (var sr = new StreamReader(fs))
    {
        // Code here
    }
}

Visual studio m'avertit que je dispose de plusieurs fois. Donc, ma question est la suivante: quelle serait la bonne façon d’écrire ceci:

using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
    var sr = new StreamReader(fs);
    // do stuff here
}

Ou devrais-je le faire de cette façon (ou une autre variante non mentionnée).

var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);

using (var sr = new StreamReader(fs))
{
    // Code here
}

J'ai cherché plusieurs questions dans StackOverflow mais je n'ai pas trouvé quelque chose qui traitait directement de la meilleure pratique pour cette combinaison.

Je vous remercie!

37
JHubbard80

Voici comment Microsoft recommande le faire. Il est long et encombrant, mais sûr:

FileStream fs = null;
try
{
    fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
    using (TextReader tr= new StreamReader(fs))
    {
        fs = null;
        // Code here
    }
}
finally
{
    if (fs != null)
        fs.Dispose();
}

Cette méthode veillera toujours à ce que tout soit mis au rebut, quelles que soient les exceptions pouvant être levées. Par exemple, si le constructeur StreamReader lève une exception, la FileStream sera toujours correctement supprimée.

39
Dan

Visual studio m'avertit que je dispose de plusieurs fois.

Vous l'êtes, mais c'est bien. La documentation de IDisposable.Dispose se lit comme suit:

Si la méthode Dispose d'un objet est appelée plusieurs fois, il doit ignorer tous les appels après le premier. L'objet ne doit pas lancer d'exception si sa méthode Dispose est appelée plusieurs fois.

Sur cette base, l'avertissement est bidon et mon choix serait de laisser le code tel quel et de supprimer l'avertissement.

13
user743382

Comme la réponse de Dan ne semble fonctionner qu'avec StreamWriter, je pense que cela pourrait être la réponse la plus acceptable. (La réponse de Dan avertira toujours deux fois les personnes disposées avec StreamReader - comme le mentionnent Daniel Hilgarth et exacerbatedexpert, StreamReader dispose du flux de fichiers)

using (TextReader tr = new StreamReader(new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
{
    string line;
    while ((line = tr.ReadLine()) != null)
    {
        // Do work here
    }
}

Ceci est très similaire à la réponse de Daniel Hilgarth, modifiée pour appeler avec dispose via l'instruction Using de StreamReader car il est maintenant clair que StreamReader appellera de disposer de FileStream (selon tous les autres articles, documentation référencée)

Mettre à jour:

J'ai trouvé ce post. Pour ce que cela vaut . Est-ce que la suppression du lecteur de flux ferme le flux?

5
JHubbard80

Oui, la bonne façon serait d'utiliser votre première alternative:

using (FileStream fs = new FileStream(filePath, FileMode.Open,
                                      FileAccess.Read, FileShare.ReadWrite)) 
{ 
    TextReader tr = new StreamReader(fs); 
    // do stuff here 
} 

La raison est la suivante:
La suppression de la StreamReader ne supprime que la FileStream;.

Votre seconde option (juste le "using" interne) n'est pas une solution car elle laisserait la variable FileStream s'il n'y avait pas d'exception dans le constructeur de la variable StreamReader.

2
Daniel Hilgarth

C'est parce que votre façon d'utiliser StreamReader élimine le flux lorsqu'il est éliminé. Donc, si vous disposez également du flux, il est éliminé deux fois. Certains considèrent cela comme une faille dans StreamReader--, mais c'est tout de même là. Dans VS 2012 (.NET 4.5), la variable StreamReader permet de ne pas supprimer le flux, avec un nouveau constructeur: http://msdn.Microsoft.com/en-us/library/gg712952

1
exacerbatedexpert

Deux solutions:

A) Vous faites confiance à Reflector ou à la documentation et vous savez que *Reader et *Writer vont fermer le *Stream sous-jacent. Mais attention: cela ne fonctionnera pas en cas d’exception levée. Donc, ce n'est pas la manière recommandée:

using (TextReader tr = new StreamReader(new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
{
    // Code here
}

B) Vous ignorez l'avertissement car la documentation indique The object must not throw an exception if its Dispose method is called multiple times. C'est la méthode recommandée, car il est recommandé de toujours utiliser using et en toute sécurité en cas d'exception:

[SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")]
internal void myMethod()
{
    [...]
    using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
    using (TextReader tr = new StreamReader(fs))
    {
        // Code here
    }
}
1
Cœur

Compte tenu de toutes les absurdités générées par cette question (parfaitement légitime!), Ce serait ma préférence:

FileStream fs = null;
TextReader tr= null;
try
{
    fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
    tr= new StreamReader(fs);
    // Code here
}
finally
{
    if (tr != null)
        tr.Dispose();
    if (fs != null)
        fs.Dispose();
}

Les liens ci-dessous illustrent parfaitement la syntaxe légale. OMI, cette syntaxe "utiliser" est de loin préférable à "utiliser" imbriquée. Mais j’admets - cela ne résout pas la question initiale:

A MON HUMBLE AVIS...

0
paulsm4