I was taught that structs should almost always be immutable, so this unusual behaviour of record classes vs. record structs threw me off guard.
Using a record class...
record class Person(string FirstName, string LastName);
Person p = new("John", "Smith");
p.FirstName = "Jack" // Not allowed!
Using a record struct...
record struct Person(string FirstName, string LastName);
Person p = new("John", "Smith");
p.FirstName = "Jack" // Fine!
Using a readonly record struct...
readonly record struct Person(string FirstName, string LastName);
Person p = new("John", "Smith");
p.FirstName = "Jack" // Now allowed!
Why are non readonly record structs mutable by default, and why doesn't the same behaviour apply for record classes?
Edit: I guess what I'm asking here is, why is the syntax... weird?
For example, it would seem more logical like:
record class - mutable reference type with value semantics.readonly record class - immutable reference type with value semantics.record struct - mutable value type with value semantics.readonly record struct - immutable value type with value semantics.Taken directly from a Microsoft MVP Engineer İlkay İlknur:
Most of the generated code looks similar to the record classes. However, there is one difference between record structs and record classes. The generated properties in record structs are mutable by default. The reason behind this decision was to be consistent with tuples. Tuples are like anonymous record structs with similar features. On the other hand, struct mutability does not carry the same level of concern as class mutability does. That's why the C# team decided to make record struct properties mutable by default. However, if you need immutable record structs, you can use the readonly keyword.
record-structs-in-csharp
If you use the positional syntax for defining a record class, it's properties are immutable
record class Point(int X, int Y);
It defaults to having property accessory of {get; init;}
If you declare this in a traditional way (without positional parameters), you can specify your property accessors:
record class Point
{
int X {get; set;}
int Y {get; set;}
}
You can mix and match these two styles, but bear in mind that if you provide any positional parameters, they will become the parameters on the auto implemented constructor, and any other properties will not be set.
I realise this doesn't actually answer the question as to why this behaviour exists, I'm just hoping that people that read this won't come away with the conclusion that record class is immutable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With