Tidbits

Wrapper to keep streams open

Recently I had to work with a CryptoStream in .NET Standard 2.0. Unfortunately (in Standard 2.0), we have no available constructor overload for keeping the passed Stream open when the CryptoStream is disposed.
Alternative solutions I have seen include creating a custom CryptoStream, playing with fields using reflections or simply not disposing the CryptoStream directly. What I’m sure several people have thought off, but which I didn’t see at that time, was a custom Stream implementation for the stream we pass along:

/// <summary>
/// A simple wrapper to prevent streams from being closed/disposed by other code
/// </summary>
public class KeepOpenStreamWrapper : Stream
{
    /// <summary>
    /// The original stream this instance wraps around
    /// </summary>
    public Stream WrappedStream { get; }
 
    /// <inheritdoc />
    public override bool CanRead => WrappedStream.CanRead;
 
    /// <inheritdoc />
    public override bool CanSeek => WrappedStream.CanSeek;
 
    /// <inheritdoc />
    public override bool CanWrite => WrappedStream.CanWrite;
 
    /// <inheritdoc />
    public override long Length => WrappedStream.Length;
 
    /// <inheritdoc />
    public override long Position
    {
        get => WrappedStream.Position;
        set => WrappedStream.Position = value;
    }
 
    private KeepOpenStreamWrapper(Stream stream) => WrappedStream = stream;
 
    /// <summary>
    /// Creates a new instance of <see cref="KeepOpenStreamWrapper"/> for the given <see cref="Stream"/>
    /// </summary>
    /// <param name="stream"></param>
    /// <returns>A new instance wrapping around <paramref name="stream"/>, or null if <paramref name="stream"/> is null</returns>
    public static KeepOpenStreamWrapper Wrap(Stream stream) => stream is null ? null : new KeepOpenStreamWrapper(stream);
 
    /// <inheritdoc />
    public override void Close()
    {
        //Do nothing
    }
 
    /// <inheritdoc />
    public override void Flush() => WrappedStream.Flush();
 
    /// <inheritdoc />
    public override long Seek(long offsetSeekOrigin origin) => WrappedStream.Seek(offsetorigin);
 
    /// <inheritdoc />
    public override void SetLength(long value) => WrappedStream.SetLength(value);
 
    /// <inheritdoc />
    public override int Read(byte[] bufferint offsetint count) => WrappedStream.Read(bufferoffsetcount);
 
    /// <inheritdoc />
    public override void Write(byte[] bufferint offsetint count) => WrappedStream.Write(bufferoffsetcount);
 
    /// <inheritdoc />
    protected override void Dispose(bool disposing)
    {
        //Do nothing
    }
}

Very simple implementation. Essentially all Stream methods/properties are redirected into the actual stream. Only Close and Dispose are special, in that they don’t do anything at all.
You can still close/dispose the original stream, as long as you use its reference. CryptoStream would only get the wrapper though, meaning the stream was not closed after disposing it.

Leave a comment