Record Types In C# 10

Introduction

In this article, we will understand record types in C# 10. The Record types are introduced in C# 9. With C# 10, Microsoft has introduced some changes to make coding simple and easy. We will look at these changes along with understanding record type.

Immutable properties

Before understanding Record types, we need to understand what immutable properties are.

Immutable properties are the properties that do not allow assignment after the initialization of the types. Below is the syntax for defining Immutable properties.

Public string FirstName { get; init;}

Prerequisites

To use features described in this article, we need to have below Prerequisites

  1. Visual Studio 2022
  2. Project with Target framework .Net 6.

Record Types

Record types are the value type or reference type object which has built in support for immutable properties. We can create record types with a class or with structures. In C# 10, Microsoft added support for declaring records with the positional declaration. 

Let’s see how to declare a record with positional syntax.

Record Class

Positional parameters syntax

public record Employee(string EmployeeId, string FirstName, string LastName);

Initializing object

Employee employee = new("E001", "John", "Deo");

Standard property syntax

public record Employee
{
    Public string EmployeeId {get; init;}
    Public string FirstName {get; init;}
    Public string LastName {get; init;}
}

Initializing object

Employee employee = new Employee()
{
    EmployeeId = "E001",
    FirstName = "John",
    LastName = "Deo"
};

Record Structures

Positional parameters syntax

public readonly record struct Employee(string EmployeeId, string FirstName, string LastName);

Initializing object

Employee employee = new("E001", "John", "Deo");

Standard property syntax

public readonly record struct Employee
{
    public string EmployeeId {get; init;}
    public string FirstName {get; init;}
    public string LastName {get; init;}
}

Initializing object

Employee employess = new Employee()
{
    EmployeeId = "E001",
    FirstName = "John",
    LastName = "Deo"
};

By default, all the members of record need to be immutable but in exceptional cases, we can add mutable members also to Record with the below syntax

Record Class

Positional parameters syntax

public record Employee(string EmployeeId, string FirstName, string LastName)
{
    public string Designation {get; set;}
}

Initializing object

Employee employee = new("E001", "John", "Deo") { Designation = "Software Engineer" };

Standard property syntax

public record Employee
{
    public string EmployeeId {get; init;}
    public string FirstName {get; init;}
    public string LastName {get; init;}
    public string Designation {get; set;}
}

Initializing object

Employee employee = new Employee()
{
    EmployeeId = "E001",
    FirstName = "John",
    LastName = "Deo",
    Designation = "Software Engineer"
};

The record structs can also be mutable, but it does not support mixed syntax.

Positional parameters syntax

public record struct Employee(string EmployeeId, string FirstName, string LastName, string Designation);

Initializing object

Employee employee = new("E001", "John", "Deo", "Software Engineer");

Standard property syntax

public record struct Employee
{
  Public string EmployeeId {get; init;}
  Public string FirstName {get; init;}
  Public string LastName {get; init;}
  Public string Designation {get; set;}
}

Initializing object

Employee employee = new Employee
{
    EmployeeId = "E001",
    FirstName = "John",
    LastName = "Deo",
    Designation = "Software Engineer"
};

Benefits of using record types

  1. Build-in support for immutability with positional syntax.
  2. Built-in support for formatting for display.
  3. Support for Non-destructive mutation.
  4. Support for inheritance with record classes.

Build-in support for immutability

The record has built-in support for immutability. By default, all properties are defined with init. So they can’t be changed after initialization.

Built-in support for formatting for display

The record types have built-in support for ToString() method. Here ToString() method prints the names and values of public properties and fields as below.

<record type name> { <property name> = <value>, <property name> = <value>, ...}

Example:

Employee employee1 = new("E001", "John", "Deo");
Console.WriteLine(employee1);
// output: Employee { EmployeeId = E001, FirstName = John, LastName = Deo }

As per requirement, we can tweak this behavior by overriding the ToString() method.

Support for Non-destructive mutation

By using With expressions, the record supports non-destructive mutations. When we use the With expressions to copy an existing record instance, with specified properties and fields modified.

Below is an example of the same.

public record Employee(string EmployeeId, string FirstName, string LastName);

Code in Main Method,

Employee employee1 = new("E001", "John", "Deo");
Console.WriteLine(employee1);
// output: Employee { EmployeeId = E001, FirstName = John, LastName = Deo }
Employee employee2 = employee1 with {
    EmployeeId = "E002", FirstName = "Allen"
};
Console.WriteLine(employee2);
// output: Employee { EmployeeId = E002, FirstName = Allen, LastName = Deo } 

Support for inheritance with record classes

The record supports inheritance with record classes. But record structs do not support inheritance. We will explore this topic in more detail in the next article.

Summary

In this article, we learned about record types, including how to declare record class and record structs and the benefits of using record types.

References

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/record#built-in-formatting-for-display