Consider the following executable example:
namespace MyNamespace;
public record struct Record()
{
public bool DoSomething { get; set; } = false;
public void SetDoSomething(bool newValue)
{
DoSomething = newValue;
}
}
public static class Program
{
public static readonly Record MyObject = new();
public static void Main()
{
MyObject.SetDoSomething(true);
Console.WriteLine($"MyObject.DoSomething: {MyObject.DoSomething}");
/* Output:
* false - current version
* true - if MyObject is not readonly or Record is defined as record class
*/
}
}
I'm trying to understand, why DoSomething
is still false, after calling the method which sets the property to true.
My guess is, that a copy gets created when calling the method. It makes sense that this does not happen if Record
is a reference type (record class). But why gets MyObject
not copied if I remove the readonly modifier?
It is called Defensive Copy, which is performed by the C# compilers to enforce the semantic of the value types, it is generally not recommended to mark readonly
on a non-readonly struct since such things will happen and further causes performance regression, there're also some similar scenarios worth mentioning, more specifically:
x.Y
causes a defensive copy of the x if:
- x is a
readonly
field and- the type of x is a non-
readonly
struct and- Y is not a field.
The same rules are applied when x is an in-parameter, ref readonly local variable or a result of a method invocation that returns a value by readonly reference.
The record
modifier here really doesn't matter, you mark the field with value type as readonly
so the compiler thinks that it should preserve the semantic, i.e., the immutability of value types through and through. When you invoke a method or access a property of that field, the compiler won't know if the method or property is actually side-effect free, so it makes a conservative decision, that is, the defensive copy to avoid it.
you can check more information at The ‘in’-modifier and the readonly structs in C# and Avoiding struct and readonly reference performance pitfalls with ErrorProne.NET
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