Advanced C# Tips: Prefer Structs for Immutable Data

Advanced C# Tips: Prefer Structs for Immutable Data

Before diving into topic, I want to begin with clarifying the terminology. Immutable data refers to data whose state cannot be modified after it has been created. Once an immutable object is created, its internal state remains constant throughout its lifetime. Any attempt to modify the object’s state results in the creation of a new object with the updated state, leaving the original object unchanged.

In C#, you can define your data using either classes or structs. The choice between a class and a struct can influence the behaviour and performance of your application. Classes are reference types and structs are value types. We also have records introduced in C# 9.0 and specifically for data. It deserves a separate post which I am going to write, but let’s keep this article’s scope limited to class and struct. For records in C# see Microsoft’s reference.The distinction between class and struct lies in how they are handled in memory. When you create a class, you’re making an object in the heap, and you work with references to that object. If you pass this object to a method, you’re passing a reference to it, not the actual object. This means if the object is changed in the method, it changes everywhere that reference is used.

When you create a struct, it is stored directly in the location where you declare it. It is created on the stack and not on the heap. When you pass a struct to a method, you are passing the whole thing, not just a reference. This makes copying more expensive, but since there’s no reference, it’s much faster to access since you’re working directly with the data.

Using structs for immutable data is efficient. Without the overhead of dynamic memory allocation (which classes require since they are reference types and live on the heap, more like a filing cabinet than the top of your desk), structs can be more performant. They are created and dealt with right at the place they are used, without the need for the garbage collector to clean them up later, which saves time and resources.

Let’s consider an example of a point on a 2D plane:

public struct Point2D
{
    public readonly double X;
    public readonly double Y;

    public Point2D(double x, double y)
    {
        X = x;
        Y = y;
    }
}

In this case, Point2D is a struct with X and Y coordinates. Once a Point2D is created, X and Y don’t change; they are immutable. This is an ideal scenario for using a struct instead of a class.

Studies and benchmarks in performance-critical applications often show that structs can offer significant memory and speed advantages. For example, a test might reveal that an operation using structs completes faster and uses less memory than the same operation using classes.

However, it is important to use structs wisely. Structs in C# can be used to represent immutable data, but there are drawbacks. When structs are passed as objects, they may need to be boxed, leading to performance overhead. Additionally, structs are allocated on the stack, which can cause memory issues if they contain a lot of data. Since structs are copied when passed as arguments or stored in collections, memory usage can increase. Unlike classes, structs cannot inherit from other structs or classes, limiting their flexibility. Also, there’s no compile-time enforcement of immutability for struct fields, which could lead to unintended mutations. Considering these limitations, using immutable classes may be preferable for managing immutable data in C#.

Suleyman Cabir Ataman, PhD

Sharing on social media:

Leave a Reply