MemoryStream in C# is a class that provides a stream implementation for in-memory data and offers several benefits over traditional file-based streams. This article will teach why, when, and how to use MemoryStream class in C#.

To download the source code for this article, you can visit our GitHub repository.

Let’s dive in.

What is MemoryStream?

MemoryStream is a class in .NET that stores data in the system’s memory. It provides a stream-based mechanism and is used to handle data efficiently. 

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!

MemoryStream implements the Stream interface. Therefore, it implements methods and properties that allow us to read, write, and seek data in the system’s memory. It also provides a buffer that is used to store the data. Additionally, we can access the data stored in the buffer both sequentially or randomly.

MemoryStream Advantages

MemoryStream has several advantages over other methods of storing data. For one, it is highly efficient, user-friendly, secure, flexible, and reliable. Additionally, we can use it in many applications, such as storing data in memory, processing data, storing large amounts of data, sending data over the network, and accessing data in a database.

One of the main advantages of using MemoryStream is its high performance. Since it works directly with memory, it can also read and write data more quickly than other stream mechanisms. If you would like to learn more about Stream-related topics, please check out our article on StreamWriter and StreamReader

The second advantage of the MemoryStream class is the simplicity of its implementation. Through our examples, we will see how simple it is to create and use MemoryStream in our projects. 

Another advantage is that MemoryStream supports random access. That means we can access data in any order rather than just reading or writing it sequentially.

Finally, since a MemoryStream works with in-memory data, we can use it to manipulate data without additional I/O operations.

How to Create MemoryStream in C#?

Creating and using MemoryStream is relatively easy. MemoryStream is a class that implements the Stream interface, providing methods and properties that allow us to read, write, and seek data in the system’s memory.

MemoryStream has several overloaded constructors:

public MemoryStream();
public MemoryStream(byte[] buffer);
public MemoryStream(int capacity);
public MemoryStream(byte[] buffer, bool writable);
public MemoryStream(byte[] buffer, int index, int count);
public MemoryStream(byte[] buffer, int index, int count, bool writable);
public MemoryStream(byte[] buffer, int index, int count, bool writable, bool publiclyVisible);

The possible parameters are:

  • buffer – an array of unsigned bytes used to create the stream
  • capacity – the initial size of the internal array in bytes
  • index – start position in the buffer at which the stream begins
  • count – the length of the stream
  • writable – determines if the stream supports writing
  • publiclyVisible – if true, GetBuffer(), a method that returns the unsigned byte array from which the stream was created is enabled 

It might be useful to note that MemoryStream implements an IDisposable interface. However, it does not hold any resources that should be disposed of. In practice, it is not necessary to call the Dispose() method on it or use the class inside a using statement. For more detail regarding this, please check the official documentation.

Now, let’s see some examples of this.

First, let’s create a class Constructors:

public static class Constructors
{
    public static MemoryStream SimpleConstructor() => 
        new MemoryStream();

    public static MemoryStream ByteArrayConstructor(byte[] bytes) => 
        new MemoryStream(bytes);

    public static MemoryStream FullConstructor(byte[] bytes, int count) => 
        new MemoryStream(bytes, 0, count, true, true);
}

Here we define three methods we will use to create MemoryStream objects.

Then, to make it easier for us to view MemoryStream properties, let’s define a method ShowMemoryStreamProperties():

public static string ShowMemoryStreamProperties(MemoryStream memoryStream, string comment = "")
{
    var sb = new StringBuilder();
    if (!string.IsNullOrEmpty(comment))
        sb.AppendLine($"{comment}\n--------------------------");

    sb.AppendLine($"{"Length:",-20}{memoryStream.Length}");
    sb.AppendLine($"{"Capacity:",-20}{memoryStream.Capacity}");
    sb.AppendLine($"{"CanRead:",-20}{memoryStream.CanRead}");
    sb.AppendLine($"{"CanSeek:",-20}{memoryStream.CanSeek}");
    sb.AppendLine($"{"CanWrite:",-20}{memoryStream.CanWrite}");
    sb.AppendLine($"{"CanTimeout:",-20}{memoryStream.CanTimeout}");
    sb.AppendLine($"{"publiclyVisible:",-20}{memoryStream.TryGetBuffer(out _)}");

    return sb.ToString();
}

This method returns a string that shows the values of MemoryStream properties.

Let’s start with the basic constructor and analyze MemoryStream properties:

var memoryStream = Constructors.SimpleConstructor();
var displayProperties = Methods.ShowMemoryStreamProperties(memoryStream,
    "Simple Constructor");

The result of the ShowMemoryStreamProperties() method is:

Simple Constructor
--------------------------
Length:             0
Capacity:           0
CanRead:            True
CanSeek:            True
CanWrite:           True
CanTimeout:         False
publiclyVisible:    True

Here we can see that by default, our MemoryStream has the Length and Capacity of zero. Furthermore, we can see the parameters CanRead, CanWrite, and CanSeek set to true. Finally, we can see the parameter CanTimeout is set to false, and the parameter publiclyVisible is set to true, which consequently means that the GetBuffer() method is enabled.

Let’s see how to create a MemoryStream from byte array:

var phrase1 = "How to Use MemoryStream in C#";
var phrase1Bytes = Encoding.UTF8.GetBytes(phrase1);

memoryStream = Constructors.ByteArrayConstructor(phrase1Bytes);
displayProperties = Methods.ShowMemoryStreamProperties(memoryStream,
    "Constructed From byte array");

Our new MemoryStream properties are:

Constructed From byte array
--------------------------
Length:             29
Capacity:           29
CanRead:            True
CanSeek:            True
CanWrite:           True
CanTimeout:         False
publiclyVisible:    False

In this case, publiclyVisible parameter is set to false. Consequently, this means its GetBuffer() method is disabled. 

Finally, let’s see the last overloaded MemoryStream constructor:

memoryStream = Constructors.FullConstructor(phrase1Bytes, phrase1Bytes.Length - 10);
displayProperties = Methods.ShowMemoryStreamProperties(memoryStream,
    "Constructed Writable from byte array with GetBuffer() enabled");

And the properties display is:

Constructed Writable from byte array with GetBuffer() enabled
--------------------------
Length:             19
Capacity:           19
CanRead:            True
CanSeek:            True
CanWrite:           True
CanTimeout:         False
publiclyVisible:    True

The constructor attributes set the Length, Capacity, and publiclyVisible parameters as expected.

Writing to MemoryStream in C#

Once we have a MemoryStream object, we can use it to read, write and seek data in the system’s memory.

Let’s see how we can write data to the MemoryStream object.

First, let’s define the data we want to write:

var phrase1 = "How to Use MemoryStream in C#";
var phrase1Bytes = Encoding.UTF8.GetBytes(phrase1);
var phrase2 = " - explanation with examples";
var phrase2Bytes = Encoding.UTF8.GetBytes(phrase2);

We define two strings and the byte arrays created from these strings. Now let’s define our MemoryStream object and write byte arrays to it:

memoryStream = Constructors.SimpleConstructor();
Methods.WriteToMemoryStream(memoryStream, phrase1Bytes);
Methods.WriteToMemoryStream(memoryStream, phrase2Bytes);
displayProperties = Methods.ShowMemoryStreamProperties(memoryStream,
    "Writing to MemoryStream");

Here are the properties of our MemoryStream object:

Writing to MemoryStream
--------------------------
Length:             57
Capacity:           256
CanRead:            True
CanSeek:            True
CanWrite:           True
CanTimeout:         False
publiclyVisible:    True

Compared with the simple constructor example, we notice Length and Capacity parameters are changed after writing the data to the MemoryStream object. 

It is important to note this works only if the MemoryStream does not have a set size. Attempt to write more data than the MemoryStream object capacity will fail since the MemoryStream is not extensible:

[Fact]
public void WhenWritingOverTheCapacity_ThenFailure()
{
    var memoryStream = Constructors.ByteArrayConstructor(new byte[10]);
    var addBytes = new byte[20];
    Assert.Throws(() => memoryStream.Write(addBytes, 0, addBytes.Length));
}

Reading from MemoryStream in C#

To read data from the MemoryStream, we define a method ReadFromMemoryStream():

public static List ReadFromMemoryStream(MemoryStream memoryStream)
{
    var phrases = new List();

    var buffer = new byte[10];
    memoryStream.Position = 0;
    memoryStream.Read(buffer, 0, 10);
    phrases.Add(Encoding.UTF8.GetString(buffer));
   
    buffer = new byte[20];
    memoryStream.Seek(10, SeekOrigin.Begin);
    memoryStream.ReadAtLeast(buffer, 20);
    phrases.Add(Encoding.UTF8.GetString(buffer));

    buffer = new byte[27];
    memoryStream.Seek(-27, SeekOrigin.End);
    memoryStream.ReadExactly(buffer, 0, 27);
    phrases.Add(Encoding.UTF8.GetString(buffer));

    return phrases;
}

Here we read data from the MemoryStream in parts using methods Read(), ReadAtLeast(), and ReadExactly(). 

Read() reads a block of bytes from the current stream, writes it to the buffer, and advances the position within the stream. It reads up to the specified number of bytes but does not block the stream if fewer bytes are available.  It takes three parameters, buffer as a byte array, offset which defines at which position to begin storing data, and count, the maximum number of bytes to read.

ReadAtLeast() is an extension method and it takes two parameters, buffer as a byte array and minimumBytes. It reads at least minimumBytes from the current stream into the buffer and advances the position in the stream by the same amount. It will block the stream until the minimum number of bytes is available.

Finally, ReadExactly() is also an extension method that reads exactly the required number of bytes, blocks the stream until all the bytes are available, and may throw an exception if the end of the stream is reached before all the bytes are read. It takes the same parameters as the Read() method.

Highlighted lines show different ways to set the current position within the MemoryStream object.

For example, to seek the data in the MemoryStream, we can set the Position property with the Seek() method. This method takes two parameters, an Offset and a SeekOrigin. The Seek() method will seek the specified offset from the specified SeekOrigin and return the new position.

Loading a File Using MemoryStream in C#

We often use MemoryStream when working with project resources. 

Let’s use MemoryStream to load the image from the resource:

public static void LoadImageFromResources()
{
    var imageMemoryStream = Constructors.ByteArrayConstructor(Resources.Image);
    File.WriteAllBytes("Image.jpg", imageMemoryStream.ToArray());
}

After loading the image data to MemoryStream, we use it to save the image to the file system.

Serialization and Deserialization Using MemoryStream in C#

Another useful usage of MemoryStream is object serialization and deserialization.

Let’s define a simple Person class:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
}

To serialize an object, we define a SerializeObject() method:

public static byte[] SerializeObject(Person person)
{
    var memoryStream = Constructors.SimpleConstructor();
    using var writer = new BinaryWriter(memoryStream);
    writer.Write(person.FirstName);
    writer.Write(person.LastName);
    writer.Write(person.Age);

    return memoryStream.ToArray();
}

Here we create a MemoryStream object and populate it with the data from the Person instance using BinaryWriter. 

Similarly, to deserialize this data to an object, we define a method DeserializeObject():

public static Person DeserializeObject(byte[] serializedData)
{
    Person deserializedPerson;
    var memoryStream = Constructors.ByteArrayConstructor(serializedData);
    using var reader = new BinaryReader(memoryStream);
    deserializedPerson = new Person
    {
        FirstName = reader.ReadString(),
        LastName = reader.ReadString(),
        Age = reader.ReadInt32()
    };

    return deserializedPerson;
}

Let’s see these methods in action:

var person = new Person
{
    FirstName = "Jack",
    LastName = "Black",
    Age = 30
};
byte[] serializedData = Methods.SerializeObject(person);
var deserializedPerson = Methods.DeserializeObject(serializedData);

Conclusion

In this article, we learned about MemoryStream and analyzed its advantages over similar methods of working with the data. Furthermore, we saw how to use it to read, write and manipulate data in an easy, secure, and reliable way.

In conclusion, if you are looking for an efficient and reliable way to handle data in your applications, MemoryStream is a great choice.

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!