0

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.

  1. Is it for consistency reasons?
  2. Is it to simplify the compiler job by having a single rule to apply blindly without further code analysis?
  3. 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.

Pragmateek
  • 13,174
  • 9
  • 74
  • 108

0 Answers0