Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Who owns wrapped streams (e.g. TextWriter) in .NET?

I've recently encountered an error "ObjectDisposedException: Cannot access a closed Stream"

[ObjectDisposedException: Cannot access a closed Stream.]
    System.IO.MemoryStream.Write(Byte[] buffer, Int32 offset, Int32 count) +10184402
    System.Security.Cryptography.CryptoStream.FlushFinalBlock() +114
    System.Security.Cryptography.CryptoStream.Dispose(Boolean disposing) +48

when using code following the format:

using (var stream = new MemoryStream())
{
    using (var hashStream = new CryptoStream(stream,
                                    new SHA256Managed(), CryptoStreamMode.Write))
    using (var writer = new TextWriter(hashStream))
    {
        writer.Write("something");
    }
    // ^-- Exception occurs on hashStream Dispose
    //     While naively I assumed that TextWriter.Dispose wouldn't touch the
    //     underlying stream(s).
    return stream.ToArray();
}

So the exception is caused because the Dispose of the TextWriter Disposes the Stream (hashStream) that is wrapped. My questions are thus:

  1. Does this convention applied (with default constructors/arguments) to all stream in .NET?

    Is there canon discussing this resource usage pattern? For instance, can it be assumed that the CryptoStream would have closed the MemoryStream? I know the answer, and there are other questions specifically about this, but I would like it addressed in terms of a design guideline if there is such.

  2. Where is such behavior documented?

    I can't find "ownerships" discussed in the TextWriter(stream) or CryptoStream constructors - surely I am just looking at the wrong location. (Update: apparently I fail at reading, as pointed out by itsme86 this is documented in the TextWriter constructor documentation.)

  3. What is the universal accepted method to write such code?

    That is, the underlying stream needs to be read (at the end of all operations, and thus still open) while all the nested streams should be closed/flushed completely - a simple CryptoStream.Flush is not sufficient, for instance.

like image 318
user2864740 Avatar asked Apr 18 '14 21:04

user2864740


2 Answers

This is specifically mentioned in the StreamWriter() documentation.

The StreamWriter object calls Dispose() on the provided Stream object when StreamWriter.Dispose is called.

like image 124
itsme86 Avatar answered Oct 01 '22 12:10

itsme86


After reading the using statement C# spec and looking around some of the implemented streams (memory, file, etc..) i see that the default behavior is to dispose the underlying streams when calling Dispose(). There are certain streams where you can explicitly state that you dont want to dispose of the underlying stream, like in DeflateStream:

public DeflateStream(Stream stream, CompressionLevel compressionLevel, bool leaveOpen)

leaveOpen Type: System.Boolean true to leave the stream object open after disposing the DeflateStream object; otherwise, false.

Of course, you might work around the disposal by not using the using statement, or perhaps implementing a WrapperStream class which wrap your stream and doesn't dispose the underlying stream.

like image 45
Yuval Itzchakov Avatar answered Oct 01 '22 12:10

Yuval Itzchakov