Records in C#
Photo by Eric Krull on Unsplash

C# 9 introduced records to the language, I have explained what a record is and how to use it in my previous article How to Use Records in C# – Part 1. If you don’t know what a record is, it’s a good idea to read Part 1 first to not leave something behind and to know what features we get with records.

C# 10 added further changes to enhance the usefulness of record types, albeit making them slightly more complex. However, I will clarify and simplify these changes in this article.

Changes to Records in C# 10

C# 10 broke the existing record type into two subcategories of record struct and record class, which means that now we have a new record struct type that is different than what we had in C# 9, and the old one is called record class.

In the same way, record (record class) came to enhance the class functionalities, now the record struct type came to add more or less the same functionalities to the struct type that we had in C#.

Record class will be a reference type object while record struct will make a value type instance. However, both of them follow the value-based equality implementation.

Syntax

So, If you want to create a record struct, you should do it like the following code.

// Positional Parameters
public record struct Student(string FirstName, string LastName, int Age);

and If you want to create a class struct you can either use the class struct or record keyword.

public record Student(string FirstName, string LastName, int Age);

// OR

public record class Teacher(string FirstName, string LastName, int Age);

Record struct and class struct can be declared using either the Positional Parameters approach or the No Positional Parameters approach.

// No Positional Parameters
public record struct Student
{
    public string FirstName { get; init; }
    public string LastName { get; init; }
    public int Age { get; init; }
}

public record class Teacher
{
    public string FirstName { get; init; }
    public string LastName { get; init; }
    public int Age { get; init; }
}

ToString()

They both implement the same ToString() format.

Record Struct Immutability

Record struct is not immutable by default when created by the positional parameters approach like the way record class is and you have to add readonly keyword to make it immutable if you need that functionality.

// immutable 
public readonly record struct Student(string FirstName, string LastName, int Age);

// not immutable 
public record struct Student(string FirstName, string LastName, int Age);

Non-immutable example:

Immutable example:

Comparing Record Struct and Struct

Structs don’t have any implementation for equality by default and that’s one of the features that record structs have over regular structs.

The following images show how you will get a compiler error if you try to compare two struct’s instances.

This code shows how the record struct has a default definition for equality

Inheritance

In the record struct type, you can only have interface inheritance, unlike the record class type which you can have the interface and a base record class inheritance.

The following is an example of how inheritance works for record struct.

public interface HighschoolStudent
{
    public string GiveLecture();
}

public record struct Student(string FirstName, string LastName, int Age) : HighschoolStudent
{
    public string GiveLecture()
    {
        throw new NotImplementedException();
    }
}

The following screenshot shows how it will give you an error if you try to inherit from another record struct.

As you can see in the screenshot below, the same inheritance would work fine for a record class.

Summary

Overall, the functionality of records didn’t change in C# 10, and we just added the record struct, which has some extra features over the regular struct the same way the record class is compared to class. If you are interested in seeing what code the compiler generates for a record struct, you can check it here.