Sure. This is a perfect case for the Decorator Pattern:
Basically you create a class that
- inherits from
Stream
(the abstract class you are decorating)
- has a constructor that accepts a single instance of that base class
Then you override all the methods and properties, passing the call through to the decorated instance. If the method or property has knowledge of the stream's position or length, you apply the appropriate adjustment as needed.
Edited to note: It looks like you need to decorate the abstract stream as shown below (no way to create a filestream instance without actually opening a file).
Here's a [truncated] example of the decorator itself:
class OffsetStreamDecorator : Stream
{
private readonly Stream instance ;
private readonly long offset ;
public static Stream Decorate( Stream instance )
{
if ( instance == null ) throw new ArgumentNullException("instance") ;
FileStream decorator= new OffsetStreamDecorator( instance ) ;
return decorator;
}
private OffsetStreamDecorator( FileStream instance )
{
this.instance = instance ;
this.offset = instance.Position ;
}
#region override methods and properties pertaining to the file position/length to transform the file positon using the instance's offset
public override long Length
{
get { return instance.Length - offset ; }
}
public override void SetLength( long value )
{
instance.SetLength( value + offset );
}
public override long Position
{
get { return instance.Position - this.offset ; }
set { instance.Position = value + this.offset ; }
}
// etc.
#endregion
#region override all other methods and properties as simple pass-through calls to the decorated instance.
public override IAsyncResult BeginRead( byte[] array , int offset , int numBytes , AsyncCallback userCallback , object stateObject )
{
return instance.BeginRead( array , offset , numBytes , userCallback , stateObject );
}
public override IAsyncResult BeginWrite( byte[] array , int offset , int numBytes , AsyncCallback userCallback , object stateObject )
{
return instance.BeginWrite( array , offset , numBytes , userCallback , stateObject );
}
// etc.
#endregion
}
Usage is pretty straightforward, something along these lines:
using ( Stream baseStream = new FileStream( @"c:\foo\bar\somefile.dat" , FileMode.Open , FileAccess.Read , FileShare.Read ) )
{
// establish your offset
baseStream.Seek( 100 , SeekOrigin.Begin ) ;
using ( Stream decoratedStream = OffsetStreamDecorator.Decorate( baseStream ) )
{
// now you have a stream that reports a false position and length
// which you should be able to use anywhere a Stream is accepted.
PassDecoratedStreamToSomethingExpectingAVanillaStream( decoratedStream ) ;
}
}
Easy! (except for all the boilerplate involved)