In this tutorial, you will learn about records in C#, a reference type introduced in C# 9 and further expanded in C# 10 with the introduction of record structs, a value type version of records. Records are specifically designed to encapsulate data and provide built-in functionality for immutability and value-based equality.
Defining Records
You can define records using the record
keyword, and there are two distinct ways to declare properties for a record: positional parameters and standard property syntax.
Record Classes (Reference Types)
Here's an example of a record class with positional parameters:
public record Person(string FirstName, string LastName);
And the equivalent using standard property syntax:
public record Person
{
public required string FirstName { get; init; }
public required string LastName { get; init; }
};
Record Structs (Value Types)
C# 10 introduced record structs, which are value types with similar functionality to record classes. Here's an example of a record struct with positional parameters:
public readonly record struct Point(double X, double Y, double Z);
And the equivalent using standard property syntax:
public record struct Point
{
public double X { get; init; }
public double Y { get; init; }
public double Z { get; init; }
}
Mutable Records
Although records are primarily intended for immutable data models, you can create records with mutable properties and fields.
public record Person
{
public required string FirstName { get; set; }
public required string LastName { get; set; }
};
Record structs can also be mutable, offering more flexibility:
public record struct Point
{
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
}
Deconstruction
Records support deconstruction, allowing you to effortlessly separate their properties into variables.
var u = new User("John", "Doe", 980);
(string fname, string lname, int sal) = u;
Console.WriteLine($"{fname} {lname} earns {sal} per month");
record User(string FirstName, string LastName, int Salary);
Records as Data Transfer Objects (DTOs)
Records can be an excellent choice for Data Transfer Objects (DTOs) because they provide a concise way to define data structures with built-in immutability and value-based equality.
Here's an example of using a record as a DTO:
public record BookDto(int Id, string Title, string AuthorName);
public record BookDetailDto(int Id, string Title, int Year, decimal Price, string AuthorName, string Genre);
Compared to using classes for DTOs, records generate value-based equality checks (see below), init-only properties, and a JSON-like format for ToString()
with less code. This makes records an efficient and convenient choice for defining DTOs in your applications.
Value-Based Equality
As previously mentioned, record types implement value-based equality by default, meaning that two instances of a record with the same values are considered equal.
The Bottom Line
In this tutorial, you learned about the fundamentals of records in C#, including defining record classes and record structs, creating mutable records, deconstruction, and value-based equality. You also learned a few practical scenarios where using record types can be helpful.
Don't stop learning!
There is so much to discover about C#. That's why I am making my favorite tips and tricks available for free. Enter your email address below to become a better .NET developer.
Did you know?
Our beautiful, multi-column C# reference guides contain more than 150 tips and examples to make it even easier to write better code.
Get your cheat sheets