user3772119
user3772119

Reputation: 494

How to StreamReader same stream in two methods?

I am trying to read from a .json file in two different methods, like so:

void mainMethod() 
{
    Stream theFile = Assembly.GetExecutingAssembly().GetManifestResourcesStream("pathToFile");

    Method1(theFile);

    Method2(theFile);

}

void Method1(Stream file)
{
   using (StreamReader fileUsage_1 = new StreamReader(file))
   {
      //do stuff with file
   }
}

void Method2(Stream file)
{
   using (StreamReader fileUsage_2 = new StreamReader(file))
   {
      //do stuff with file
   }
}

Running the mainMethod() gives the exception: Stream was not readable. in Method2() I believe the problem is that Method2() is trying to read theFile when it is still being used in Method1(). I thought the using blocks closed the file automatically? How can I customize the way I am reading theFile in Method1() and Method2() so they don't "overlap"?

Upvotes: 0

Views: 1099

Answers (4)

Chad Dienhart
Chad Dienhart

Reputation: 5204

Open the StreamReader once and set the position when in each Method. This way the order of calling methods is not fixed. When you first open the StreamReader the position is set to the beginning by default, so the first call to Seek(0,SeekOrigin.Begin) is redundant but allows for the order of calling to be interchanged in the future.

    void mainMethod() 
    {
        Stream theFile = Assembly.GetExecutingAssembly().GetManifestResourcesStream("pathToFile");
        using (StreamReader fileUsage = new StreamReader(theFile))
        {
            Method1(fileUsage);
            Method2(fileUsage);
        }
    }

    private static void Method1(StreamReader fileUsage)
    {
        if (fileUsage != null && fileUsage.BaseStream.CanSeek && fileUsage.BaseStream.CanRead)
        {
            fileUsage.BaseStream.Seek(0, SeekOrigin.Begin);
            fileUsage.DiscardBufferedData();
            Console.WriteLine(fileUsage.ReadLine());
        }
    }

Upvotes: 2

PaulBinder
PaulBinder

Reputation: 2052

Call me crazy...but can't you simply abstract the using statement out of the methods and into the main program? Like so. Edited to include reseting of position if you choose to do so. (Can go to Here for more information on discarding buffer data)

namespace ConsoleApplication12
{
    class Program
    {
        static void Main(string[] args)
        {

            Stream theFile = Assembly.GetExecutingAssembly().GetManifestResourceStream("ConsoleApplication12.test.txt");
            using (StreamReader fileUsage_1 = new StreamReader(theFile))
            {
                Method1(fileUsage_1);
                ResetPosition(theFile, fileUsage_1); // If needed
                Method2(fileUsage_1);
            }

            Console.ReadLine();
        }

        private static void Method2(StreamReader fileUsage)
        {
            var test = fileUsage.ReadLine();
        }

        private static void Method1(StreamReader fileUsage)
        {
            var test = fileUsage.ReadLine();
        }

        private static void ResetPosition(Stream s, StreamReader sr)
        {
            s.Position = 0;
            sr.DiscardBufferedData();
        }
    }
}

Upvotes: 1

Leslie Marshall
Leslie Marshall

Reputation: 101

Set the Stream.Position as previously stated. set it thus:

file.Seek(System.IO.SeekOrigin.Begin);

Also the Using blocks on the StreamReader close the underlying stream when they exit. You can use the File.ReadAllText method to Open, Read and Close(Dispose) of a File all with that single call:

string jsonString = File.ReadAllText(filePath);

Upvotes: 0

Alexei Levenkov
Alexei Levenkov

Reputation: 100547

Re-reading whole stream is possible if:

  1. stream is seekable (stream.CanSeek == true)
  2. readers don't close stream

You generally have no control over 1, but you can copy whole srteam into seek-able one if needed (i.e. using Stream.Copy to MemoryStream).

To stop reader from closing stream - use constructor that does not take ownership of stream and as reslut will not close stream after using. I.e. StreamReader(...,bool leaveOpen).

Finally you need to seek stream to position where other reader need to start (often beginning) with something like Stream.Position = 0;

Notes

  • if you just need to continue reading stream with Method2 from the point where Method1 left you don't need to seek, just make sure readers don't close stream.
  • alternatively you can explicitly not close readers (removing using / .Close call), but such code would look wrong and when one "fixes" it by adding using it will no longer work correctly.

Upvotes: 0

Related Questions