10

In my program, I am basically reading in a file, doing some processing to it, and then passing it back to the main program as a memorystream, which will be handled by a streamreader. This will all be handled by a class beside my main.

The problem is, when I return the memory stream from my method in another class, the "canread" variable is set to false, and thus causes the streamreader initialization to fail.

Below is an example of the problem happening (though in here I'm writing to the memorystream in the other class, but it still causes the same error when i pass it back.

In the class named "Otherclass":

public static MemoryStream ImportantStreamManipulator()
{
   MemoryStream MemStream = new MemoryStream();

   StreamWriter writer = new StreamWriter(MemStream);
   using (writer)
   {
       //Code that writes stuff to the memorystream via streamwriter

       return MemStream;
   }
}

The function calls in the main program:

MemoryStream MStream = Otherclass.ImportantStreamManipulator();
StreamReader reader = new StreamReader(MStream);

When I put a breakpoint on the "return MemStream", the "CanRead" property is still set to true. Once I step such that it gets back to my main function, and writes the returned value to MStream, the "CanRead" property is set to false. This then causes an exception in StreamReader saying that MStream could not be read (as the property indicated). The data is in the streams buffer as it should be, but I just can't get it out.

How do I set it so that "CanRead" will report true once it is returned to my main? Or am I misunderstanding how MemoryStream works and how would I accomplish what I want to do?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Xantham
  • 1,829
  • 7
  • 24
  • 42

3 Answers3

30

This is the problem:

using (writer)
{
    //Code that writes stuff to the memorystream via streamwriter

    return MemStream;
}

You're closing the writer, which closes the MemoryStream. In this case you don't want to do that... although you do need to flush the writer, and rewind the MemoryStream. Just change your code to:

public static MemoryStream ImportantStreamManipulator()
{
   // Probably add a comment here stating that the lack of using statements
   // is deliberate.
   MemoryStream stream = new MemoryStream();

   StreamWriter writer = new StreamWriter(stream);
   // Code that writes stuff to the memorystream via streamwriter

   writer.Flush();
   stream.Position = 0;
   return stream;
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Jon, what happens if `writer` is GC'd before `stream` is used? – Kevin Brock Jul 17 '12 at 18:47
  • 2
    @KevinBrock: Then it's just garbage collected, and any buffered data is lost. `StreamWriter` doesn't have a finalizer, AFAIK. – Jon Skeet Jul 17 '12 at 18:50
  • That was exactly it. I thought that returning it would send out a non-closed version before the using would dispose of the streamwriter. I guess I was thinking that the return would return a copy of the MemoryStream, not a reference for some reason. I also thought that leaving the ImportantStreamManipulator method via a return would automatically dispose of the streamwriter anyway, so I wasn't sure this was possible. Will the streamwriter be disposed sometime later in the program via the GC, or will it only be disposed once I dispose of the MemoryStream or close the program? – Xantham Jul 17 '12 at 18:51
  • @Xantham: You don't *need* the `StreamWriter` to be disposed. It doesn't have any unmanaged resources. It will be garbage collected at any point after its final use - potentially straight after the `Flush` call. – Jon Skeet Jul 17 '12 at 18:52
2

The StreamWriter takes ownership of the memory stream and when the using statement ends, the MemoryStream is also closed.

See Is there any way to close a StreamWriter without closing its BaseStream?.

Community
  • 1
  • 1
Kevin Brock
  • 8,874
  • 1
  • 33
  • 37
0

As others have stated, the problem is that the Stream is closed when the StreamWriter is closed. One possible way to deal with this is to return a byte array rather than a MemoryStream. This avoids having potentially long running objects that must be disposed by the garbage collector.

public static void Main()
{
    OutputData(GetData());
}

public static byte[] GetData()
{
    byte[] binaryData = null;

    using (MemoryStream ms = new MemoryStream())
    using (StreamWriter sw = new StreamWriter(ms))
    {
        string data = "My test data is really great!";

        sw.Write(data);
        sw.Flush();

        binaryData = ms.ToArray();
    }

    return binaryData;
}

public static void OutputData(byte[] binaryData)
{
    using (MemoryStream ms = new MemoryStream(binaryData))
    using (StreamReader sr = new StreamReader(ms))
    {
        Console.WriteLine(sr.ReadToEnd());
    }
}

Another method is to copy the Stream to another stream prior to returning. However, this still has the problem that subsequent access to it with a StreamReader will close that stream.

public static void RunSnippet()
{
    OutputData(GetData());
}

public static MemoryStream GetData()
{
    MemoryStream outputStream = new MemoryStream();

    using (MemoryStream ms = new MemoryStream())
    using (StreamWriter sw = new StreamWriter(ms))
    {
        string data = "My test data is really great!";

        sw.Write(data);
        sw.Flush();

        ms.WriteTo(outputStream);
        outputStream.Seek(0, SeekOrigin.Begin);
    }

    return outputStream;
}

public static void OutputData(MemoryStream inputStream)
{
    using (StreamReader sr = new StreamReader(inputStream))
    {
        Console.WriteLine(sr.ReadToEnd());
    }
}   
JamieSee
  • 12,696
  • 2
  • 31
  • 47
  • MemoryStream.ToArray() will even work when the underlying Stream is closed. You cant access the closed stream, but the data is still there. – Jan Oct 05 '18 at 08:41