Dot Net For All

Working With Stream .NET(C#)

Stream .NET(C#)

Stream in .NET has always been a topic which I want to understand in depth, which in turn should help me to program better my applications which are interacting with data present in the memory, network or hard disk which are also storage mediums and the same data I should be able to work with using the C# programming language.

In this article I want to discuss about the basics of streams in .Net platform using C# which would help you to have a solid understanding of the streams.

Defining Stream

Stream is a byte of data which can be used to read or write to the backing store which are called storage mediums. As I have already told that the storage medium can be present across the network or local disk or in the form of local memory only. We can easily say that stream holds the data present in any form to the array of bytes and can be used to transfer across the network or to manipulate it. The benefit of  using stream is the way the application can process the data. With the help of streams the data can be processed in chunks which is not the case if we don’t use the streams. In absence of stream we have to work with whole data at one shot which can lead to memory pressure on the application. There is an exception to this advantage if we are using memory stream, as in case of memory stream the data is already present in the memory. Therefore it is advised to use the memorystream only if we are dealing with small data.

Streaming Architecture

Just for the basic understanding, please have a look at the below figure which gives us an idea of the architecture of the streaming of .NET

Stream Arcihtecture

All the streams class inherit from the common base class and the class is System.IO.Stream.

It means that no matter whatever is the backing store stream class, it will have some of the methods derived from the Stream class.

We can group the functions of the Stream class in three categories.

  1. Reading And Writing
  2. Seeking
  3. Buffering,Flushing and Disposing

Stream Pointer – Stream pointer indicates the current position in the stream

Stream pointer

Suppose we have a stream of bytes as shown in the figure and our application wants to read these bytes. Initially the position of the stream will be set to 0 as shown in the below figure.

Moving the pointer in Stream class

Now suppose our application reads 3 bytes from the stream in that case the position is again reset to 3 as shown in the below figure

In the next iteration again the application reads the next three bytes and now the position is set to 6 ,which is equal to the length of the stream. Once the end of the stream is reached, the position of the pointer is equal to the length of the stream as shown in the below figure..

As discussed previously, the functions of the Stream class can be grouped into three. I will discuss all these one by one.

Please not that I have written all the code examples using C# programming language

Reading from Stream

Reading part has mainly three functions as shown below along with their descriptions.

  1. Read() – Have look at the signature of the function as shown below.
        public abstract int Read(byte[] buffer, int offset, int count);

    The function reads the bytes of the stream in the byte array parameter, advances the pointer position by the number of bytes read and returns the number of bytes read, or 0 if the end of the stream is encountered.
    The offset parameter is the position in the buffer at which to start placing the read data.
    The count is the maximum number of bytes to be read from the stream. This actually depends on the stream. If the bytes are less in that case the number of bytes read is less.
    Though there are other version of the same function are present which help to read the byte asynchronously as shown below.

            public Task<int> ReadAsync(byte[] buffer, int offset, int count); 
            public virtual Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken);
    
  2. ReadByte – Reads a byte from the stream and advances the position within the stream by one byte, or returns -1 if at the end of the stream.
    public virtual int ReadByte();
  3. CanRead – Checks if the stream supports reading. A stream is not always readable due to various reason like, the wrapper class developer of the stream class does not want its stream to read from or a stream is only appended to as shown in the below case.

Writing To Stream Class

In this part I will discuss about the write methods available in the stream class and their utilizations.

  1. Write – Write method is used to write the bytes into the stream from the buffer provided. It advances the pointer position by the number of bytes written. The method’s signature is shown below.
    public abstract void Write(byte[] buffer, int offset, int count);

    The parameters in the above signature are the buffer from which we will write data into the stream, the offset position is the index in the buffer at which writing begins.

    Just like Read, the write also has overloaded asynchronous methods, which can be helpful to write the bytes asynchronously.

            public Task WriteAsync(byte[] buffer, int offset, int count);
     public virtual Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken);
    
  2. WriteByte – Writes a single byte. It advances the position into the stream by 1 byte.
    public virtual void WriteByte(byte value);
    
  3. CanWrite – This function is again the assurance mechanism to check if the stream supports writing.

Seeking The Stream Class

As we already know from the previous discussion that data can be read or written from or to the stream in a sequential manner in which the pointer position moves as the data is read.

But there is a way in which we can read or write data using non sequential manner and that is done with the help of seeking.

MemoryStream and FileStream supports seeking, while NetworkStream does not support it.

Following are the different function and property members of the Stream class which help in seeking.

  1. Seek: Sets the pointer within the stream
     public abstract long Seek(long offset, SeekOrigin origin);
  2. SetLength: Sets the length of the stream and stream must support writing and seeking.
    If length > stream.Length then stream is expandedIf length < stream.Length then stream is truncated.
  3. Length : Returns the length of the stream. Supported only when seeking is enabled.
  4. Position : Gets or sets the pointer position within the stream. It internally uses the Seek method. Seeking must be supported by the stream to use the Position property.
  5. CanSeek : Helps to find if the stream supports seeking.

How to get the length of the Un-seekable Stream? 

As we know that we cannot use the Length property directly for the streams which are not seekable. In that case we have to read the stream in the buffer and query the length of the buffer.

 

Disposing the Stream 

As stream class uses the unmanaged resources to read or write data, it is always advisable to dispose the streams after using. For Example FileStream uses FileHandle and MemoryStream uses socket handle which are memory consuming resources and can create lot of problem if not disposed properly.

Stream can be disposed by calling explicitly Dispose() method on the stream or by using statement as shown below

using (FileStream fs = File.Create(@"C:\files\testfile3.txt"))
{

}

Conclusion:

In this article I have discussed about the basics of the streaming in .NET which should be helpful for the reader in understanding the functioning of the Stream in .NET framework.

Going further you can read about FileStream class in C# and Reading a file in C#.

Top career enhancing courses you can't miss

My Learning Resource

Excel your system design interview