As a record is read-only its state must be set at construction time.
There are 2 ways of setting the state:
- calling the primary constructor, e.g. before another constructor via
this(...)
- setting the properties inside another constructor
But it seems that directly setting the properties is ignored and only the call to the primary constructor is considered a valid way to set the initial state.
And indeed sometimes the call to the primary constructor is mandatory like when a field initializer depends on a value provided to the primary constructor, so only it can initialize the field:
record R(int N)
{
int m = N;
public R() : this(1)
{
}
}
But if there is no such dependency, why the compiler complains about this code:
record R(int N)
{
public R()
{
N = 1;
}
}
error CS8862: A constructor declared in a record with parameter list must have 'this' constructor initializer.
After the call to R()
the state is set.
- Is it for consistency reasons?
- Is it to simplify the compiler job by having a single rule to apply blindly without further code analysis?
- Is there a flaw in this initialization?
I'm coming from Why is an explicit this
constructor initializer required in records with a primary constructor? but the rationales behind this rule are not explained in the simple case of no dependency between the initial field value and the primary constructor's parameters.
Moreover I though maybe the primary constructor was doing something special, but not at all:
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
.custom instance void [mscorlib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 )
.method public hidebysig specialname rtspecialname instance void .ctor(int32 N) cil managed
{
// Code size 15 (0xf)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: stfld int32 R::'<N>k__BackingField'
IL_0007: ldarg.0
IL_0008: call instance void [mscorlib]System.Object::.ctor()
IL_000d: nop
IL_000e: ret
} // end of method R::.ctor
It does nothing more than setting the backing field of the N
property to the provided value, then calling the base Object()
constructor.