C#’s new non-record class and struct primary constructors can make your code cleaner and more concise, but if you’re not careful, they’ll ruin your app. Let’s talk about what they are, why you might want to start using them, and how to avoid the problems they can bring.
permalinkHow Constructors Started
Let’s start with a constructor that’s familiar:
Our GeoPoint
class is constructed with two parameters that are used to set two
private properties on the class. Of course, another common use case is
dependency injection, where we inject an instance of a class to separate the
concerns of this class from others.
Regardless of the use case, the pattern is the same and it’s what we’ve been writing in C# forever. But now primary constructors aim to simplify this code while also providing some interesting usage options.
permalinkHow Primary Constructors Work
Let’s take a look at our GeoPoint
class refactored to use primary
constructors in C# 12.
The constructor method has been removed and its parameters have been added to the class declaration line. Already, you can see a reduction in lines of code, but what really makes primary constructors powerful is that the parameters they specify are in scope throughout the declaring type’s entire body.
permalinkParameter Scope
In this code snippet, we extend the GeoPoint
to include expression-bodied
members that return the hemisphere and meridian of the object. Notice the
parameters are referenced like private properties of the class. That’s because
they are essentially private properties. They aren’t available publicly but can
be passed to base constructors, used to set properties, or even used in
functions or expression-bodied members.
This also means that our dependency-injected parameters look much cleaner as we don’t have to explicitly define private properties.
permalinkConstructor Overloads
One thing you’ll figure out quickly is once you’ve declared a primary
constructor, your class will no longer have an implicit parameterless
constructor. But fear not, you can always add constructor overloads with one
condition: they must call the primary constructor using the this
keyword.
Any additional constructors must call the primary constructor using the
this
keyword.
permalinkBehavior of Note
If you’re like me, you’re already thinking about how you can refactor old code to make use of this cleaner syntax, but there are a couple things you need to keep in mind before you start.
permalinkNaming Conflicts
Primary constructor parameter naming conflicts are allowed by the compiler with both fields and properties. When it occurs, the property or field will hide the primary constructor parameter. The only exception to that rule is property initialization.
permalinkDifferent Than Record Primary Constructors
Unlike record types, public properties are not generated and because of this,
the with
keyword cannot be used. Those using record types will want to account
for this by explicitly declaring properties if they’re needed.
permalinkWrap Up
The new class and struct primary constructors introduced in C# 12 are a welcome addition to our syntax and allow us to write cleaner & more concise code, but for those with experience using primary constructors with record types, you’ll need to be aware of some key differences.