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?
The behaviour you see is present not only in record structs, but also non-record structs too. Try removing the keyword
recordand the()after the nameRecord, and see the same behaviour.This is just how calling mutating methods on structs are supposed to work. When you call a mutating method on a struct variable, say
x.F(), you actually pass a reference tox, then that reference can be mutated byF.For example, if
Recordis a non-record struct, andMyObjectis notreadonly,MyObject.SetDoSomething(true);is compiled to the following IL (Try it yourself with SharpLab):ldsfldameans "load static field address". I've only found a small section of the spec that talks about this when it is talking about boxing of structs (emphasis mine):Basically, if you don't box structs (you clearly don't here!), their methods are supposed to be called by reference. No copies are supposed to be made.
On the other hand, if you call
x.F()butxisreadonly, you obviously can't translate it to the same code above, since that would mutate the field. What the compiler does, according to SharpLab, is:Basically, it loads the value of the struct to a temporary variable first, and then pass the reference of that variable to
SetDoSomething.Hence the "copy" behaviour that you see.