C#12 primary constructors: no auto-generated properties?

.NET 8, along with C#12, is now official. Last week, I upgraded one of my projects to the new version and tried using some new features.

Primary constructors in classes don’t generate auto-properties

One thing that surprised me was that the primary constructors, known from record types and now supported in classes, behaved differently than I expected.

Let’s consider the following example showing primary constructors used both in a record and in a class:

So, is there something wrong with my tooling? Or is this by design that an auto-property I am used to having generated by the C# compiler is present for records, but not for classes?

Different by design

It turns out that this difference in how records and classes behave is by design. Having properties auto-generated from primary constructor parameters is specific to records only:

When you declare a primary constructor on a record, the compiler generates public properties for the primary constructor parameters. The primary constructor parameters to a record are referred to as positional parameters. The compiler creates positional properties that mirror the primary constructor or positional parameters. The compiler doesn’t synthesize properties for primary constructor parameters on types that don’t have the record modifier.

Source: Records (C# reference)

After some thought, generating properties for records makes sense, and for classes – not necessarily.

Records are intended to be used to encapsulate data. For immutable structures, it is a typical pattern to have data initialized during construction, and later accessed via read-only properties. While records don’t force you to create an immutable type, primary constructors in records optimize for that use case and make syntax short and sweet.

Classes have wider use, and we can easily imagine e.g., classes that take dependencies as constructor parameters (following the dependency injection pattern). In such a case having all values passed via a constructor exposed as public properties is not desired. It seems reasonable that it’s not the default.

Can I make the class generate auto-property anyway?

While auto-properties are not generated by default for classes, it could be tempting to try to have them generated with an addition of an access modifier like public. This could be familiar from languages like TypeScript where it’s called Parameter Properties. It could look like:

// annotation like "public" could theoretically be used to inform compiler we want a field or property generated
public class C(public int i) {} Code language: C# (cs)

In C#12 such a feature doesn’t exist. However, it’s described in the section on “Possible extensions,” and it might appear in future versions of the language (although the section also mentions the downsides of such design choice).

So, how do we use the value from the class’s primary constructor?

Before I conclude, let me show a simple example of how we can use values from a primary constructor in class (because we cannot use it the way it works with records). Here are the equivalent implementations of a record and a class*.

* To be precise, they are equivalent regarding how a primary constructor value is exposed. There are also differences in equality comparison and deconstruction not considered here.

Summary

Primary constructors behave differently in classes than in records. I expected parity in behavior, although, after consideration, I see that it makes more sense the way it was done.

Will primary constructors be useful in classes? Honestly, I’m not confident if this new feature will lead to a simpler code in our solutions or, rather, make it less straightforward. We’ll see ourselves soon 🙂

Leave a Comment