Before we start, it is essential to know that .NET categorizes objects as value types
and reference types
. The memory is managed in a way that value-type instances are stored statically with their values on the stack memory
, while reference-type variables are stored dynamically on the heap memory
, holding references to their data. This also means that their default equality
differs in the sense it’s based on values for value-types and on pointers for reference types.
Why Record class?
Comparing value type variables of the same type and with the same values will result in equality. On the other hand, the same thing with reference type variables will result in a difference, unless you deal with IEquatable
and GetHashCode
overrides to make it happen.
C# 9
introduced a new reference type named record
, and it was meant to get rid of this costly boilerplate, stating a convenient implementation for building immutable data structures
with:
- An override of ToString().
- An override of Object.Equals(Object).
- An override of Object.GetHashCode().
- A virtual Equals method whose parameter is the record type.
- Methods for operator == and operator !=.
- Implement System.IEquatable
.
C# 10
introduced an optional keyword for evidence when a record is a class, as you will see further down.
Are Records immutable on their own?
No, but there are two ways to ensure it. The first is by using init-only setters like this:
The second way is by making a positional record
and passing an ordered list of arguments in the constructor, it will make them immutable by default:
Copy that
Once they are immutable, if defined as above, you can no longer change their values but only create a copy with new ones instead. By using with
expressions, you can set new values between the braces with properties like this:
Or simply let it empty to have a full-copy:
Structs
Struct is a very known Value Type
in the .NET world. One can say they’re a light version of classes due to the way they structure data. Because they’re value types, it makes them cheaper
in memory allocation, resulting in better performance.
However, there are other differences, like not allowing inheritance
at all. Also, when it comes to equality comparison
, there are limitations, as you can see given these two structs:
- Even the variables being of the same type and having the same values, the
==
operator isn’t available for structs, resulting infalse
. It could be only performed by using.Equals()
method. - Printing it would output its type name,
Models.Probe
.
A brand new type: Record Struct
Finally, C# 10
introduces a new value type, the record struct. Simply put, it brings many capabilities records added from classes to structs:
- Use
with
expressions. - Creation with
positional parameters
for immutability. - Equality comparison with
==
and!=
operators. - PrintMembers and ToString() methods.
Are Record Structs always immutable?
Differently from the record class type, making it a positional record won’t make the record struct immutable on its own. However, directly changing its value is still be possible:
This problem can be easily solved by using the readonly
keyword:
And your record struct becomes immutable.
Hands-On
I have written a simple example using both record class
and record struct
to use most of the theory described above.
Final thoughts
Part of our job as developers is making the right decisions, including using the proper types for our data structures. C# is in constant evolution to help ease such choices. Consider aspects like code reusability, memory allocation cost, and performance when choosing types.