Skip to content

Instantly share code, notes, and snippets.

@daiplusplus
Created March 20, 2020 11:10
Show Gist options
  • Save daiplusplus/23c990c6b5f4e8e3cb07d45da66d6afa to your computer and use it in GitHub Desktop.
Save daiplusplus/23c990c6b5f4e8e3cb07d45da66d6afa to your computer and use it in GitHub Desktop.
ProxyStream for .NET Framework, .NET Standard and .NET Core
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace WhyAreYouCopyingCodeFromGitHub
{
/// <summary>
/// <para>This class directly subclasses <see cref="System.IO.Stream"/> and overrides every virtual member. All members directly invoke their corresponding method on the wrapped <see cref="System.IO.Stream"/> object. Only the members <see cref="Stream.CreateWaitHandle"/>, <c>Stream.CreateObjRef</c> (not present in .NET Standard) and <see cref="Stream.ObjectInvariant"/> are not overridden.</para>
/// <para>This class is intended to be used as the base class for a subclass that only wants to override individual methods when proxying a <see cref="Stream"/> instead of implementing all of Stream's abstract methods. This class can also be instantiated directly and used as a preventative measure against consumers of your <see cref="System.IO.Stream"/> objects from being disposed by using the <see cref="LeaveOpen"/> constructor parameter.</para>
/// </summary>
public class ProxyStream : Stream
{
/// <summary>Constructor.</summary>
/// <param name="stream">The stream to wrap/proxy. Required. Cannot be null.</param>
/// <param name="leaveOpen">When <c>true</c> then <see cref="ProxyStream.Dispose(bool)"/> will NOT dispose of <paramref name="stream"/>, otherwise <paramref name="stream"/> will be disposed when this <see cref="ProxyStream"/> is disposed.</param>
public ProxyStream( Stream stream, Boolean leaveOpen )
{
this.Stream = stream ?? throw new ArgumentNullException(nameof(stream));
this.LeaveOpen = leaveOpen;
}
/// <summary>When <c>true</c> then <see cref="ProxyStream.Dispose(bool)"/> will NOT dispose of <see cref="ProxyStream.Stream"/>, otherwise <see cref="ProxyStream.Stream"/> will be disposed when this <see cref="ProxyStream"/> is disposed.</summary>
public Boolean LeaveOpen { get; }
/// <summary>The wrapped/proxied <see cref="System.IO.Stream"/> object.</summary>
public Stream Stream { get; }
public override Boolean CanRead => this.Stream.CanRead;
public override Boolean CanSeek => this.Stream.CanSeek;
public override Boolean CanTimeout => this.Stream.CanTimeout;
public override Boolean CanWrite => this.Stream.CanWrite;
public override Int64 Length => this.Stream.Length;
public override Int64 Position
{
get => this.Stream.Position;
set => this.Stream.Position = value;
}
public override Int32 ReadTimeout
{
get => this.Stream.ReadTimeout;
set => this.Stream.ReadTimeout = value;
}
public override Int32 WriteTimeout
{
get => this.Stream.WriteTimeout;
set => this.Stream.WriteTimeout = value;
}
public override IAsyncResult BeginRead( Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state )
{
return this.Stream.BeginRead( buffer, offset, count, callback, state );
}
public override IAsyncResult BeginWrite( Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state )
{
return this.Stream.BeginWrite( buffer, offset, count, callback, state );
}
public override Task CopyToAsync( Stream destination, Int32 bufferSize, CancellationToken cancellationToken )
{
return this.Stream.CopyToAsync( destination, bufferSize, cancellationToken );
}
public override Int32 EndRead( IAsyncResult asyncResult )
{
return this.Stream.EndRead( asyncResult );
}
public override void EndWrite( IAsyncResult asyncResult )
{
this.Stream.EndWrite( asyncResult );
}
public override void Flush()
{
this.Stream.Flush();
}
public override Task FlushAsync( CancellationToken cancellationToken )
{
return this.Stream.FlushAsync( cancellationToken );
}
public override Object InitializeLifetimeService()
{
return this.Stream.InitializeLifetimeService();
}
public override Int32 Read( Byte[] buffer, Int32 offset, Int32 count )
{
return this.Stream.Read( buffer, offset, count );
}
public override Task<Int32> ReadAsync( Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken )
{
return this.Stream.ReadAsync( buffer, offset, count, cancellationToken );
}
public override Int32 ReadByte()
{
return this.Stream.ReadByte();
}
public override Int64 Seek( Int64 offset, SeekOrigin origin )
{
return this.Stream.Seek( offset, origin );
}
public override void SetLength( Int64 value )
{
this.Stream.SetLength( value );
}
public override void Write( Byte[] buffer, Int32 offset, Int32 count )
{
this.Stream.Write( buffer, offset, count );
}
public override Task WriteAsync( Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken )
{
return this.Stream.WriteAsync( buffer, offset, count, cancellationToken );
}
public override void WriteByte( Byte value )
{
this.Stream.WriteByte( value );
}
#region Close + Dispose
/// <summary>NOTE: If <see cref="LeaveOpen"/> is true, then <see cref="Stream"/> will be closed.</summary>
public override void Close()
{
// Stream::Close() calls `Stream::Dispose( disposing: true );`
// and then `GC.SuppressFinalize(this);`.
// So there's no need for custom logic here... or is there?
if( this.LeaveOpen )
{
}
else
{
this.Stream.Close();
}
base.Close();
}
protected override void Dispose( Boolean disposing )
{
if( this.LeaveOpen )
{
}
else
{
this.Stream.Dispose();
}
// Don't call `base.Dispose` because *this* method (`Dispose(bool)` is invoked from `Stream::Dispose()` already)
//base.Dispose();
}
#endregion
// [Obsolete]
// protected override WaitHandle CreateWaitHandle()
// {
// throw new NotSupportedException();
// }
//
// [Obsolete]
// protected override void ObjectInvariant()
// {
// throw new NotSupportedException();
// }
//
// public override ObjRef CreateObjRef( Type requestedType ) // Not present in .NET Standard
// {
// return this.Stream.CreateObjRef( requestedType );
// }
#region Object virtual
public override Boolean Equals( Object obj )
{
return this.Stream.Equals( obj );
}
public override Int32 GetHashCode()
{
return this.Stream.GetHashCode();
}
public override String ToString()
{
return this.Stream.ToString();
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment