Record vs Class vs Struct and more, C# 9
Value types and reference types are the two main categories of C# types. A class is one of the keywords to declare a reference type in C#. Struct is a value type that can encapsulate data and related functionality.
I will not start explaining in depth the differences between value types and reference types but one of the differences is regarding value equality.
Value equality
Value equality means that two objects contain the same value or values.
Class
When it comes to Class
we can use the method Object.Equals
that determines whether two object instances are equal. The default implementation of Equals
supports reference equality for reference types. Reference equality means the object references that are compared refer to the same object, meaning they point to the same address on the heap.
Struct
When it comes to a Struct
we can use the ValueType.Equals(object? obj)
method that returns true
if obj
and this instance is the same type and represents the same value. This implementation uses reflection to examine all the fields and properties.
In the following example, we have a CProduct class
and a SProduct struct
both have the same properties.
When we create two identical(by properties value) instances of both, we can see that Equals
returns true
for the struct
instance and false
for the class
instance, just as we explained.
Two more things you should notice are :
- The hashCode value for two identical(by properties value) instances is the same for
struct
and different forclass
. - operator: == and != operators can’t operate on a
struct
unless the struct explicitly overloads them.
Record
C# 9.0 introduces record types
In C# 9 we are introduced to a new type: record
. It is a reference type that offers a few features such as immutability.
This is how we can declare a record:
public record RProduct(int Price, string name);
When it comes to value equality, records behave like value types:
You can see that not only the method Equals
behaves the same as for struct
, but we also have the option to use == and != operators.
In addition, the hashCode value for two identical(by properties value) instances is the same.
Built-in formatting for display
Contrary to the default implementation for ToString()
in classes
and structs
, which just returns the following :
namespae.object-type
record types have a compiler-generated ToString()
that displays the names and values of public properties and fields. ToString()
returns a string of the following format:
<record type name> { <property name> = <value>, <property name> = <value>, …}
Deconstruct Method
When creating a record the compiler creates a Deconstruct
method with an out
parameter for each positional parameter provided in the record declaration.
This is what the method will look like:
As you can see record
is a very nice feature, may not always be the right choice to use, but it is definitely a refreshment to C#. It enables us to have a “value-type” object that is actually a reference type that is also thread-safe as part of its immutability.