Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

239
Views
Why is an explicit `this` constructor initializer required in records with a primary constructor?

In C# 9 we can create positional records causing them to get a constructor, which the spec draft calls a primary constructor. We can create a custom constructor as well, but as stated in the spec:

If a record has a primary constructor, any user-defined constructor, except "copy constructor" must have an explicit this constructor initializer.

So this is disallowed:

public record A(string Foo, int Bar)
{
    public A(MyClass x)
    {
        Foo = x.Name;
        Bar = x.Number;
    }
}

and indeed causes CS8862 "A constructor declared in a record with parameter list must have 'this' constructor initializer." We have to write:

public record A(string Foo, int Bar)
{
    public A(MyClass x) : this(x.Name, x.Number) {}
}

instead. In this case this is hardly an issue, but one could imagine a much longer initialization logic that just didn't fit into the this constructor initializer.

The question is: why does this restriction exist? I'm guessing lifting it would enable a way to somehow break some of the features of records, but it's a feature new enough that I can't come up with a way to do that. Does the autogenerated primary constructor do something that is crucial for the record to work correctly, and thus it must be called?


over 4 years ago · Santiago Trujillo
3 answers
Answer question

0

This is because primary constructor parameters are a little bit special - they are in scope throughout initialization of the record. Guess what the following program prints:

System.Console.WriteLine(new Foo(Bar: 42).Baz);

public record Foo(int Bar) {
    public int Bar => 41;
    public int Baz = Bar;
}

41 or 42?

And the answer is...

drumroll please...

42!

What's going on here?

During the initialization of the record, any references to Bar don't refer to the property Bar, but to the primary constructor parameter Bar.

What this means is that the primary constructor must be called. Otherwise what would happen in this case:

System.Console.WriteLine(new Foo().Baz);

public record Foo(int Bar) {
    public Foo(){}
    public int Bar => 41;
    public int Baz = Bar; //  What is Bar here when the primary constructor isn't called.
}

Aside

The Bar parameter is only in scope during initialization. After initialization the Bar property is in scope instead. If we were to change our example ever so slightly:

System.Console.WriteLine(new Foo(Bar: 42).Baz);

public record Foo(int Bar) {
    public int Bar => 41;
    public int Baz => Bar; //Note this is `=>` not `=`
}

It would print 41.

over 4 years ago · Santiago Trujillo Report

0

Based on a quick Linqpad decompilation, yes, it looks like this constructor IS in fact doing work that otherwise might not be inferable due to not knowing how to map the type to the record properties.

public A(string Foo, int Bar)
{
    this.Foo = Foo;
    this.Bar = Bar;
    base..ctor();
}

public A(MyClass x)
    : this(x.Name, x.Number)
{
}

A record expects all properties to be initialized via the constructor. If properties could arbitrarily be mapped in an additional constructor without explicitly calling this, there would be no (immediately obvious) way to ensure that every property parameter requirement had been met. As a result, a call to this(params) is required to enforce the property mapping.

over 4 years ago · Santiago Trujillo Report

0

I am not sure I have a definitive answer, but here are my thoughts based on what I have read. This

public record MyRecord(string foo, int bar);

is equivalent to:

public class MyRecord 
{
    string foo { get; init; } // Code correction - set to init
    int bar { get; init; }
}

The constructor and properties are both inferred in the single line. While I am sure it possible to add concrete assignment to this inferred set of constructs (and maybe will in a future version), it is likely easier to work with the chaining of constructors for the first pass. As one who has used chained constructors in some classes (not usually pocos), it makes sense. But, I am not sure how many times I would overload the constructor and I don't see a huge benefit, at least architecturally, doing it the way you did.

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!