A stream is an abstraction over a sequence of bytes. You can think of it as a continuous buffer that can be read or written to. Streams are often used in conjunction with buffers to help load data into memory more efficiently. The System.IO namespace in .NET has many classes that work with streams, such as FileStream, MemoryStream, FileInfo, and StreamReader/Writer classes.
Basically, streams are classified as either byte streams or character streams, where byte streams deal with data represented as bytes and character streams deal with characters. In .NET, the byte streams include the Stream, FileStream, MemoryStream, and BufferedStream classes. The .NET character streams include TextReader, TextWriter, StreamReader, and StreamWriter.
This article illustrates the use of the BufferedStream and MemoryStream classes in C#, with relevant code examples wherever applicable. To work with the code examples provided in this article, you should have Visual Studio 2022 installed in your system. If you don’t already have a copy, you can download Visual Studio 2022 here.
Create a console application project in Visual Studio
First off, let’s create a .NET Core console application project in Visual Studio. Assuming Visual Studio 2022 is installed in your system, follow the steps outlined below to create a new .NET Core console application project in Visual Studio.
- Launch the Visual Studio IDE.
- Click on “Create new project.”
- In the “Create new project” window, select “Console App (.NET Core)” from the list of templates displayed.
- Click Next.
- In the “Configure your new project” window shown next, specify the name and location for the new project.
- Click Next.
- In the “Additional information” window shown next, choose “.NET 7.0 (Standard Term Support)” as the Framework version you would like to use.
- Click Create.
We’ll use this .NET 7 console application project to work with the BufferedStream and MemoryStream classes in the subsequent sections of this article.
What is a buffer?
A buffer represents a block of bytes in memory where you can temporarily store transient data. A buffer helps in minimizing the number of calls your application makes to read and write data from and to the file system. Buffers are useful when you need to store data that is being transferred from one computer system to another or from one program component to another.
Buffers are used in conjunction with streams to make it easier for programs to read and write data efficiently. Buffers store data temporarily so that your application need not keep re-reading the data from disk every time it is requested.
Use the BufferedStream class in C#
The BufferedStream class represents a type of stream that can buffer data before writing it to the stream. In other words, a buffered stream can read or write data into a buffer, allowing you to read or write larger chunks of data at once to improve performance. You can create buffered streams from memory streams and file streams.
When you create an instance of the BufferedStream class, you can specify the buffer size as well. The default buffer size is 4096 bytes. Reading data from a buffered stream involves reading from the buffer when you call the Read method. Even if you call Read multiple times, the data will be fetched from the stream only once.
When you write to a buffered stream, the data is written into a buffer and then flushed to the stream when you call the Flush method. This improves performance by avoiding accessing the stream for every Write call. When we use a buffer, we do not execute writes or reads until a certain number of operations have been requested.
By storing some amount of data in its internal buffer, BufferedStream can process multiple operations on the same chunk of memory without having to allocate memory again and again. This saves both time and memory consumption when creating new objects repeatedly.
Note that you can use a BufferedStream instance for either reading data or writing data, but you cannot use the same instance for both operations. BufferedStream is designed to prevent input and output from slowing down when there is no need for a buffer. A buffered stream may not even allocate an internal buffer if the read and write size is always greater than the internal buffer size.
The following code snippet shows how you can write data to a file using BufferedStream.
using (FileStream fileStream = new FileStream("D:\\MyTextFile.txt", FileMode.Create, FileAccess.ReadWrite))
{
BufferedStream bufferedStream = new BufferedStream(fileStream, 1024);
byte[] bytes = Encoding.ASCII.GetBytes("This is a sample text.");
bufferedStream.Write(bytes);
bufferedStream.Flush();
bufferedStream.Close();
}
When should you use BufferedStream? Use BufferedStream when you want to add support for buffering to an existing stream. Thus if the original stream were a network stream, the data sent to it would be cached in a small buffer before being written to or retrieved from the network stream.
Using the MemoryStream class in C#
The MemoryStream class represents a lightweight stream that allows you to write to or read from a memory buffer. The MemoryStream class supports the same methods and properties as those of the Stream class. MemoryStream provides a simple way to read or write data directly from memory, without having to allocate and deallocate memory every time you want to read or write something. This makes it faster than using other techniques that require you to reallocate memory on each use.
A memory stream is a stream that is very fast and efficient since the data resides in the memory. However, this also means that it can be easily lost if the program crashes or the computer shuts down abruptly.
The MemoryStream class is part of the System.IO namespace. It can be used to read from and write to files, network connections, and other devices that support reading and writing data. The MemoryStream class can also be used for serializing an object into a stream of bytes for storage or transmission over a network connection.
The following code snippet shows how you can write data to a memory stream in C#.
byte[] bytes = System.Text.Encoding.ASCII.GetBytes("This is a sample text.");
using (MemoryStream memoryStream = new MemoryStream(50))
{
memoryStream.Write(bytes, 0, bytes.Length);
}
When should you use MemoryStream? As its name suggests, MemoryStream is a memory-only stream. As such, it should be used only when the amount of data that needs to be cached is small enough to comfortably fit in memory.
While BufferedStream is faster and more efficient, MemoryStream is well-suited for scenarios where your application requires faster access to data. You can use the async versions of the Read and Write methods of BufferedStream and MemoryStream classes for even better performance and scalability.