# Records
In C# Records
can be used to define reference data types, similar to classes.
The main purpose for record data types is to encapsulate data. While you can add methods to records, most of the time, they are only used with data fields.
# Definition
To define a record you can use the following syntax.
This would be similar to defining a class that has three properties: FirstName, LastName and Age.
# Usage
To create an instance you need to use the constructor that will be generated for each record type:
You can access individual properties of the record, just as you would with classes.
# Difference to classes
When comparing records to classes, on the first look they seem very similar. The main difference that we can spot, is a much shorter form for declaring records.
However there are a few differences we should highlight.
# Positional syntax for property definition
You can use positional parameters to declare properties of a record and to initialize the property values when you create an instance:
The compiler will create:
- A constructor
- Init-only properties for every property you defined in the list
# Positional and normal syntax
You can also define properties as you are used to in classes.
# Immutable by default
The properties we define for records are immutable by default. Immutable means that we cannot change their values after the record instance has been created.
# Why immutability is important
-
Easier to understand
When using immutable data types the developers can more easily understand what is happening. If we know that the values of a certain reference will never change, we needn’t worry about it. Compare this to a class with simple properties where it’s much more difficult to find out which property changed at which point in time and which part of code did the change.
-
Thread safety
Types become thread-unsafe when different threads can read and modify data. The most common problem are race conditions.
When a data type is immutable, we don’t run into this problem at all. Read-only access from different threads isn’t a problem. Therefore we don’t need to take special care for making a record thread-safe.
-
Efficient change detection
When a record changes, it means that a new record is created and the old one is being replaced. So if we want to detect whether a record object has changed, we only need to compare their references instead of comparing (all) individual properties.
# Value equality
If you don’t override or replace equality methods, the type you declare governs how equality is defined:
- For
class
types, two objects are equal if they refer to the same object in memory. - For
record
types, two objects are equal if they are of the same type and store the same values.
Reference equality is required for some data models. For example, Entity Framework Core depends on reference equality to ensure that it uses only one instance of an entity type for what is conceptually one entity. For this reason, records are not appropriate for use as entity types in Entity Framework Core.
# Value equality example
# Nondestructive mutation
As record
types are immutable, we cannot change individual properties. If we still need to change properties, we have to create a new instance of the record, copying all property values and modifying those that need to change. There is a short form that helps us with that.
# Built-in formatting for display
Record types have a compiler-generated ToString
method that displays the names and values of public properties and fields. The ToString
method returns a string of the following format:
for example
# Inheritance
A record can inherit from another record. However, a record can’t inherit from a class, and a class can’t inherit from a record.
The syntax for using inheritance is very similar to those for classes:
# Summary
Given the following record:
To create a class with a similar behavior you would have to do the following:
Create init-only properties and a constructor:
Override the Equals
and GetHashCode
methods:
Override the ToString
method:
# Where to use records?
Due to their nature, records are often used for implementing CSharp_DTO