C# 12: Primary Constructors

Another new C# 12 feature might drop soon and makes its debut with the next iteration: Primary Constructors.

The blog post will talk about what a Primary constructor is, why we already have it, and what the proposal tries to change. Exciting times are ahead of us!


Disclaimer: Where it is likely that this feature will be available with C# 12, there is also a chance that it will get scrapped. We had this in the past with features like the !! operator or field keyword.


Primary constructors

Before we go into detail, let's discuss first what a primary constructor is. And you might be surprised but we have primary constructors since C# 9 to some extent, namely records. Let's have a look at a classic definition of a record:

public record Person(string Name, int Age)
{
    // We always have to call the "primary constructor"
    // We can not omit the "this(...)" call
    public Person() : this("Steven", 31) {}
}

var steven = new Person("Steven", 31);

We can see that records define their primary constructor directly in the definition. The proposal, which I will link down in the description of that blog post, extends that behavior to "regular" classes. "Regular" in quotes as records are also normal classes, but with some compiler magic to enhance the class. Before I show some code, let's see the motivation and I will directly quote from the proposal here:

The ability of a class or struct in C# to have more than one constructor provides for generality, but at the expense of some tedium in the declaration syntax, because the constructor input and the class state need to be cleanly separated.

Primary constructors put the parameters of one constructor in scope for the whole class to be used for initialization or directly as object state. The trade-off is that any other constructors must call through the primary constructor.

There is also a difference between records and the new primary constructor on any class (at least as off now). Let's have an example together:

var steven = new Person("Steven", 31);

public class Person(string name, int age)
{
    public string Name { get; set; } = name;
    
    public int Age { get; set; } = age;
    
    // Just like records we have to call this(...)
    public Person() : this("Steven", 31)
    {
    }
}

The difference is that the parameters are written in lowercase letters rather than pascal case. The reason here is simple: In contrast to records the primary constructor is more like an argument rather than the property. That is why you can see that we set those parameters to the properties.

You can see that example and can fiddle around on sharplab.io.

You might notice that in the top bar there is something like C# Next: Primary Constructors (12 Jan 2023). The reason is simple: That feature is not merged to the main branch of the Roslyn compiler yet. Currently, the test plan is open and the team is testing that feature. If it goes through that phase, we will see it landing with C# 12, which will be part of the .NET 8 release in November 2023.

Conclusion

The new primary constructor helps to make sure that it will be called. With the recent addition of required, I am not sure how well this feature will be adopted. The primary constructor, as of now, doesn't have further logic. So you can't call other functions inside your primary constructor.

Resources

5
An error has occurred. This application may no longer respond until reloaded. Reload x