Home > dotnet > Introducing the C# 11 Required Keyword

Introducing the C# 11 Required Keyword

A new .NET 7 language feature that helps reduce boilerplate constructor code

by
Published: Last Updated on

In this article we’ll explore the new C# 11 required keyword that was introduced this week and see how it helps provide alternative ways of safely configuring objects.

Note: this content is also available in video form.

This feature, like init-only properties and a number of previous language features, is aimed at improving the way we initialize objects that does not rely on constructor parameters.

C# 11 Prerequisites

Before we go too much farther, let’s discuss how to get set up with C# 11 so you can play with the required keyword in C# 11 along with the other new features.

C# 11 is an incremental upgrade to the C# programming language that was released earlier this week. Like most language upgrades C# 11 adds a number of new features to the C# syntax and is backwards compatible with prior versions of C#.

In order to use C# 11 and follow along with this code you will need:

  • Visual Studio 2022 17.4.0 or greater installed
  • A project targeting .NET 7 or greater
  • The project’s LangVersion set to 11 or latest

The easiest way of upgrading is to open the Visual Studio Installer and click upgrade to ensure your Visual Studio environment is up to date. If you do not yet have Visual Studio 2022 or greater installed, you can always find the community edition available online.

Exploring a Typical C# 10 Class

Take a look at the following C# class representing an article:

public class Article
{
    public Article(string title, string subtitle, string author, DateTime published)
    {
        Title = title;
        Subtitle = subtitle;
        Author = author;
        Published = published;
    }

    public Article(string title, string author, DateTime published)
    {
        Title = title;
        Author = author;
        Published = published;
    }
    
    public string Title { get; set; }
    public string? Subtitle { get; set; }
    public string Author { get; set; }
    public DateTime Published { get; set; }

    public override string ToString()
    {
        if (string.IsNullOrWhiteSpace(Subtitle))
        {
            return $"{Title} by {Author} ({Published.ToShortDateString()})";
        }

        return $"{Title}: {Subtitle} by {Author} ({Published.ToShortDateString()})";
    }
}

Note that this class has a number of properties, a pair of constructors, and a ToString override.

This class declares the Title, Author, and Published properties as non-nullable (no ? after their data type). This means that these values should never be null.

The class also has a Subtitle property declared as a nullable string (string?), which indicates that null values are acceptable for this property.

To support this, the class offers two different constructors: one that takes in a subtitle and one that does not.

As it stands, this class serves its purpose and ensures that all required properties are set via constructor arguments. However, there are a few things about this class that might irritate developers.

The Problem with Constructors

First of all, you might notice that this class devotes a significant portion of its total lines of code to constructors. These constructors don’t actually do much other than map their arguments to different properties.

Secondly, as the number of properties in this class grows, these constructors will likely grow as well. It is likely that new arguments will be added to the constructor signatures. These additional arguments will lead to new lines of code being written to map their values into properties. Additionally, the addition of new properties may lead to the desire to add new overloads of the constructor, further expanding the impact of constructors on the class.

Third of all, these constructors don’t add much to the class beyond “boilerplate” logic of wiring arguments into the properties they go with. They are largely noise and opportunities for small mistakes to occur, introducing bugs.

Fourth of all, calling these constructors can look a little nasty at times.

Take the following sample invocation for example:

Article thisArticle = new("Introducing the C# 11 Required Keyword", "A new language feature that helps reduce boilerplate constructor code", "Matt Eland", new DateTime(2022, 11, 12));

Not only is this a very long line of code, but it takes a moment of time to mentally map what property each parameter corresponds to.

Furthermore, as we add new required constructor parameters, this line will only grow longer.

Many folks are fine with these things, and for them, that’s fine. However the C# language has been trying to reduce “constructor bloat” in recent years for those who want these capabilities.

Improving Construction with Object Initializers

One way that we’ve tried to reduce the need for long constructors has been to rely more on inline object initializers.

If we were to use an inline initializer, we could rewrite the class to only have a default constructor as follows:

public class Article
{
    public string Title { get; set; }
    public string? Subtitle { get; set; }
    public string Author { get; set; }
    public DateTime Published { get; set; }

    public override string ToString()
    {
        if (string.IsNullOrWhiteSpace(Subtitle))
        {
            return $"{Title} by {Author} ({Published.ToShortDateString()})";
        }

        return $"{Title}: {Subtitle} by {Author} ({Published.ToShortDateString()})";
    }
}

This would then let us create it as follows:

Article thisArticle = new()
{
    Title = "Introducing the C# 11 Required Keyword",
    Author = "Matt Eland",
    Published = new DateTime(2022, 11, 12)
};

Console.WriteLine(thisArticle);

Note: the new() syntax here is using the target-typed new feature introduced in C# 9 and is not strictly necessary for this example. The code could also have said new Article()

This code works great, and this is a minimal way of constructing an instance of Article that has all required members configured.

However, there’s nothing explicitly forcing the caller to set all required properties, which results in the CS8618 compiler warning in the Article class definition as shown below:

This is effectively stating that it is technically possible to create an article without an Author or Title property. Even though these properties are defined as non-nullable in the class, their default values will be null so if the object initializer doesn’t set them, their values will remain null and this may lead to errors.

To illustrate this, the following code is still valid for constructing an instance of an Article:

Article thisArticle = new()
{
    // Note: no Title or Author properties
    Published = new DateTime(2022, 11, 12)
};

Console.WriteLine(thisArticle);

This is where the new C# 11 required keyword can help us.

The C# 11 Required Keyword

With C# 11 we can now add the required keyword to properties to indicate that they must be set during initialization.

The code for this is as follows:

public class Article
{   
    public required string Title { get; set; }
    public string? Subtitle { get; set; }
    public required string Author { get; set; }
    public required DateTime Published { get; set; }

    public override string ToString()
    {
        if (string.IsNullOrWhiteSpace(Subtitle))
        {
            return $"{Title} by {Author} ({Published.ToShortDateString()})";
        }

        return $"{Title}: {Subtitle} by {Author} ({Published.ToShortDateString()})";
    }
}

Now if you try to create a new instance of an Article but don’t set the required members, you get a CS9035 compiler error as shown below:

This shows us how the C# 11 required keyword can enforce non-null constraints on object initializations for classes that would prefer to use initializers instead of complex constructors.

Unlike past solutions, the required keyword makes the compiler responsible for enforcing setting of non-nullable properties. This ultimately leads to better quality since it now becomes impossible not to set required properties when initializing a new instance.

If you’re interested in more details on the C# 11 required keyword, I’d encourage you to check out the feature specification.

Before we wrap up, I want to stress a point here: we are not getting rid of constructors. Every class still has at least one constructor. Constructors remain an integral part of dotnet languages. We’re now simply aiming at using the default constructor more and more and doing our initialization outside of it with inline object initializers.

Final Thoughts on the C# 11 Required Keyword

Overall, I think I like the new required keyword, but I do have a few reservations.

On the plus side, I like the idea of relying less on custom constructors and having more of your code oriented towards solving business problems than initializing state.

I also like the fact that object initializers tend to be easier to read than constructors.

However, this does come with a price.

For one, every new keyword and alternative way of doing something we add to the C# language makes the language harder to learn. New learners, like my bootcamp students, now have a new keyword that they may encounter in their travels.

When new learners do encounter the C# 11 required keyword they will be forced to learn what it means, when to use it, and why they might choose to use it or not use it.

Additionally, this growing trend against complex constructors risks dividing C# users into those who prefer using object initializers vs those who prefer complex constructors. This will push organizations to have more principled stances in their style guides and force library authors to support both workflows.


Overall, I think the required keyword is interesting and I’m going to try a project or two without relying much on custom constructors to see how it feels.

I’d love to hear your opinion about the required keyword and constructors vs initializers in general.

Author

  • Matt Eland

    After several decades as a software engineer and engineering manager, Matt now serves as a software engineering instructor at Tech Elevator where he gets to raise up future developers and unleash them upon the world to build awesome things. Matt is an Azure Data Scientist and AI Engineer Associate, runs a data science blog and YouTube channel, is currently pursuing a master's degree in data analytics, and helps organize the Central Ohio .NET Developer Group. In his copious amounts of spare time, Matt continues to build nerdy things and looks for ways to share them with the community.

7 comments

Gene Huh November 13, 2022 - 12:52 pm

6th paragraph of final thoughts, you keyworded keyword instead of required.
I liked the article and never knew there wasn’t a null safe with initializers vs constructors. Thanks Matt!

Reply
Matt Eland November 13, 2022 - 1:56 pm

So I did. Fixed!

Reply
The Best C# 11 Feature You Don't Need November 13, 2022 - 8:19 pm

[…] earlier this week. Most of the new features were incremental language improvements such as the new required […]

Reply
The Best C# 11 Feature You Don't Need November 14, 2022 - 12:26 am

[…] earlier this week. Most of the new features were incremental language improvements such as the new required […]

Reply
Dew Drop – November 14, 2022 (#3807) – Morning Dew by Alvin Ashcraft November 14, 2022 - 7:26 am

[…] Introducing the C# 11 Required Keyword (Matt Eland) […]

Reply
The Morning Brew - Chris Alcock » The Morning Brew #3586 November 15, 2022 - 1:00 am

[…] Introducing the C# 11 Required Keyword – Matt Eland […]

Reply
Creating Custom C# Exception Types November 20, 2022 - 12:49 am

[…] how the C# programming language has matured, I wanted to show a second example using the required keyword and bypassing providing a custom constructor […]

Reply

Leave a Reply

Related Content

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More