10
votes

Pourquoi éliminer StreamReader fait-il un flux illisible?

J'ai besoin de lire un flux deux fois, du début à la fin.

Mais le code suivant jette un ObjectDisposeception: impossible d'accéder à un fichier fermé code> Exception. P>

string fileToReadPath = @"<path here>";
using (FileStream fs = new FileStream(fileToReadPath, FileMode.Open))
{
    using (StreamReader reader = new StreamReader(fs))
    {
        string text = reader.ReadToEnd();
        Console.WriteLine(text);
    }

    fs.Seek(0, SeekOrigin.Begin); // ObjectDisposedException thrown.

    using (StreamReader reader = new StreamReader(fs))
    {
        string text = reader.ReadToEnd();
        Console.WriteLine(text);
    }
}


2 commentaires

Envisagez également d'utiliser System.IO.FILE.ReadallText () dans des situations comme celles-ci. C'est plus simple.


@Dave Markle: Tu as raison. Je l'ai dit comme un court exemple. En fait, dans le code réel, les flux que je traite peuvent être très gros, le premier lecteur leur lit la ligne par ligne, puis le flux est copié par octet vers un autre flux.


6 Réponses :


7
votes

Le but de Dispose () est de nettoyer les ressources lorsque vous avez terminé avec le flux. La raison pour laquelle le lecteur impacte le le flux est dû au fait que le lecteur ne fait que filtrer le flux, et donc disposer que le lecteur n'a pas de sens, sauf dans le contexte de celui-ci en chaisant l'appel à la Source Stream aussi.

Pour corriger votre code, utilisez simplement un lecteur tout le temps: xxx

modifié pour traiter les commentaires ci-dessous : < / p>

Dans la plupart des situations, vous n'avez pas besoin d'accéder au flux sous-jacent que vous le faites dans votre code ( fs.seek ). Dans ces cas, le fait que streamdreader chaîne son appel dans le flux sous-jacent vous permet d'économiser sur le code en n'utilisant pas de déclaration pour le flux. Par exemple, le code ressemblerait à: xxx


2 commentaires

En fait, je demande pourquoi disposer () sur le lecteur affecte le flux . Je ne comprends pas votre réponse. Cela signifie-t-il que les lecteurs disposent uniquement d'un objectif de nettoyer les données de flux? Alors pourquoi même streamreader implémente Idisposable , s'il ne fait rien utile (puisque dispose le flux lui-même fera dans tous les cas. )?


@Mainma, le but de Dispose () est toujours pour vous assurer que toutes les ressources associées au indiquées sont nettoyées. Étant donné que le lecteur et le flux représentent la même entité éliminant l'un a le même effet que l'élimination de l'autre.



2
votes

en utilisant définit une portée, en dehors de laquelle un objet sera disposé, donc la objetdisposeception . Vous ne pouvez pas accéder au contenu de StreamReader en dehors de ce bloc.


0 commentaires

16
votes

Cela se produit parce que le StreamReader reprend "propriété" du flux. En d'autres termes, il se rend responsable de la fermeture du flux source. Dès que votre programme appelle Dispose ou Fermer (laissant le à l'aide de de la portée de l'instruction dans votre cas), il dispose également du flux source également. Appeler fs.dispose () dans votre cas. Donc, le flux de fichiers est mort après avoir quitté le premier en utilisant le bloc . C'est un comportement cohérent, toutes les classes de flux dans .net qui enveloppent un autre flux se comportent de cette façon.

Il y a un constructeur pour streamreader qui permet de dire qu'il ne possède pas posséder le flux source. Il n'est cependant pas accessible à partir d'un programme .NET, le constructeur est interne.

Dans ce cas particulier, vous résoudriez le problème en n'utilisant pas le à l'aide de -Statement du streamreader . C'est cependant un détail de mise en œuvre assez velu. Il y a sûrement une meilleure solution à votre disposition mais le code est trop synthétique pour proposer un vrai.


0 commentaires

0
votes

Dispose () sur le parent disposera () Tous les flux appartenant. Malheureusement, les flux n'ont pas détach () méthode, vous devez donc créer une solution de contournement ici.


0 commentaires

0
votes

Je ne sais pas pourquoi, mais vous pouvez laisser votre streamrired non déconposé. De cette façon, votre flux sous-jacent ne sera pas disposé, même lorsque celui-ci a été collecté.


1 commentaires

Bien sûr. Mais il n'est pas trop intuitif de ne pas mettre à l'aide d'un StreamReader pour moi, et cela violera probablement les règles FXCOP. Au fait, la question à l'origine de cette question est apparue lorsque j'ai refoulé et un code ancien où nous manquions complètement.



1
votes

Je suis d'accord avec votre question. Le plus gros problème avec cet effet secondaire intentionnel est lorsque les développeurs ne le savent pas et sont aveuglément à la suite de la "meilleure pratique" de l'entourage d'un streamerrieur avec un en utilisant code>. Mais cela peut causer des bugs très difficiles à suivre les bugs lorsqu'il est sur la propriété d'un objet de longue durée, le meilleur (pire?) Que j'ai vu est xxx pré>

Le développeur n'avait aucune idée L'intrustream est désormais hosté pour tout lieu futur qui s'attend à ce qu'il soit là. P>

Évidemment, une fois que vous connaissez les internes, vous savez éviter le en utilisant code> et simplement lire et réinitialiser la position. . Mais je pensais qu'un principe fondamental de la conception de l'API était d'éviter les effets secondaires, en particulier de ne pas détruire les données que vous agissez. Rien non inhérent à une classe qui est supposée être un "lecteur" devrait effacer les données qu'il se lit lorsqu'il est fait "en utilisant". L'élimination du lecteur doit libérer les références au flux, non pas effacer le flux lui-même. La seule chose à laquelle je puisse penser, c'est que le choix devait être fait car le lecteur modifie un autre état interne du flux, comme la position du pointeur de recherche, qu'elles supposaient que si vous envisagez de l'utiliser autour de cela, vous allez intentionnellement être fait avec tout. D'autre part, comme dans votre exemple, si vous créez un flux, le flux lui-même sera dans un en utilisant code>, mais si vous lisez un flux créé en dehors de votre méthode immédiate, Il est présomptueux du code pour effacer les données. P>

Ce que je fais et dit à nos développeurs de faire sur les instances de flux que le code de lecture ne crée pas explicitement est ... P>

// save position before reading
long position = theStream.Position;
theStream.Seek(0, SeekOrigin.Begin);
// DO NOT put this StreamReader in a using, StreamReader.Dispose() clears the stream
StreamReader sr = new StreamReader(theStream);
string content = sr.ReadToEnd();
theStream.Seek(position, SeekOrigin.Begin);


1 commentaires

Un meilleur design pour un rationniste aurait été d'avoir un argument de constructeur préciser si cela devrait s'approprier le flux. Il existe de nombreuses situations dans lesquelles le code peut ouvrir un flux, créer un lecteur pour cela, remettre le lecteur à quelque chose qui lira les données à un moment futur, puis n'a plus d'utilisation pour le flux. Dans cette situation, le flux devrait être disposé lorsque le lecteur est effectué avec elle, mais le créateur du flux n'a peut-être aucune idée de ce qui sera.