In this article, we’ll discuss the differences between record struct and record class in C#. First, we will shortly explain each of the types. Afterward, we’ll show and describe each difference between them. 

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

Let’s start. 

Record Struct and Record Class in C#

C# is built upon two categories of types: value types and reference types. Each manages memory differently, and their equality is different. 

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!
To dive into these differences, read our article Value vs Reference Types in C# – In Depth Guide.

A class is the reference type. That implies that when we pass an object as an argument to a method, we pass the reference to that object. If we create two objects of the same class with the same properties’ values, these objects won’t be the same. Struct is, on the other hand, value type. When we pass a struct as an argument to a method, we pass a copy of this struct. Analogously, two value types are equal when their values are the same. 

Since C# 9, we have a new record type. A record type is the reference type. But, unlike other reference types, two records are equal if they are the same class and if the value of every field is equal. We use record type when we want an immutable reference type with value-based equality. Records are ideal for working with data structures. 

If you want to learn more about the record type, read our article Records in C#.

From C# 10, we also have a record struct type. It is the value type, but it adds some features record added to the class types. 

Differences Between Record Struct and Record Class in C#

Let’s first define two similar types. First, the record class type:

public record class PersonRecordClass(string Name, DateTime BirthdayDate, int YearsOld, string[] PhoneNumbers);

We define a record class type named PersonRecordClass. It implements the primary constructor, and the compiler generates public properties for the constructor’s parameters. The primary constructor parameters are also called positional parameters. We could omit the class keyword and use only the record keyword. 

Want to hear more about primary constructors? Check out our article Primary Constructors for Classes and Structs in C#.

After that, we define a similar record struct type PersonRecordStruct

public record struct PersonRecordStruct(string Name, DateTime BirthdayDate, int YearsOld, string[] PhoneNumbers);

Now we’re ready to discuss some of the differences.

Allocation/Deallocation

A record class is a reference type. It is allocated on the heap, and the garbage collector will deallocate it when there is no reference to it anymore. 

A record struct is a value type. It is allocated on the stack or inline with the containing type. Consequently, it will be deallocated when the stack unwinds or when the containing type is deallocated. 

Default Value

Let’s examine the default values of the defined record class and the record struct

public static PersonRecordClass? GetRecordClassDefaultValue()
{
    return default(PersonRecordClass);
}

public static PersonRecordStruct? GetRecordStructDefaultValue()
{
    return default(PersonRecordStruct);
}

We can print the return values of these methods: 

Record class default value is null
Record struct default value is PersonRecordStruct 
{ Name = , BirthdayDate = 01/01/0001 00:00:00, YearsOld = 0, PhoneNumbers =  }

The default value of the record class is null, as is the case for any other reference type. The default value of the record struct is the instantiated object with the default values for each property. The Name property is a string, and PhoneNumbers is an array. Thus, the default values of both reference types are null.

Inheritance

A record class can inherit from another record. It can’t be inherited from a class:

public record class PersonWithAddressRecordClass(string Name, DateTime BirthdayDate, int YearsOld, 
    string[] PhoneNumbers, string Address) : PersonRecordClass(Name, BirthdayDate, YearsOld, PhoneNumbers);

The record class PersonWithAddressRecordClass inherits from the previously defined PersonRecordClass. The derived class doesn’t hide the base class’s properties and only generates the properties for new parameters in the constructor. In our example, that would be a string Address

A record struct can’t inherit other classes.

Immutability

If we declare a record class with a primary constructor, it is immediately immutable. We won’t be able to change any of its properties after construction. Another way to achieve a record class immutability is by using the init-only setters:

public record class PersonRecordClassInitSetters
{
    public required string Name { get; init; }
    public DateTime BirthdayDate { get; init; }
    public int YearsOld { get; init; }
    public required string[] PhoneNumbers { get; init; }
};

The PersonRecordClassInitSetters record class has the same property as the PersonRecordClass. The difference is that instead of using a primary constructor, we defined these properties as init-only, and we won’t be able to set them after the record construction. 

On the other hand, a record struct is not immutable out of the box. It even won’t be immutable when defined with positional parameters. But there is an easy way to make it. We only have to add readonly keyword in the record struct definition: 

public readonly record struct PersonRecordStructReadOnly(string Name, DateTime BirthdayDate, int YearsOld, string[] PhoneNumbers);

Let’s finish up with a brief overview of the differences.

Overview of Differences Between Record Struct and Record Class in C#

record class and record struct are pretty similar, but they also provide some crucial differences.

Let’s have a short look at them once again:

Record classRecord struct
Allocationheapstack
Deallocationgarbage collectionstack unwind
Default valuenullstruct with default field values
Inheritanceyesno
Immutabilityno with primary constructor or init-only settersno with readonly keyword

Conclusion

.NET introduces the record type to provide value equality to reference types. Records are primarily used for lightweight, immutable data structures. 

After introducing the record class, .NET also introduces record struct and adds some valuable features similar to the record class.

Generally, we’ll choose the record struct to pass values instead of references. On the other hand, the record class is ideal for defining the Data Transfer Objects (DTOs), as we want to have only one instance of this data structure in almost every case. We will also choose the record class when our types have to support inheritance. 

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