Non-nullable properties in C# 11

How to declare non-nullable immutable properties in records, classes and structs (C# 11).

ยท

3 min read

After taking a break from blogging, I am excited to be back and try something new. As an experiment, I have decided to focus on writing shorter articles, which hopefully, will allow me to share some of my thoughts, ideas and tips in a more concise and digestible manner. Let's see how it goes. Shall we?

Problem

First, take a look at this simple code:

public record User
{
    public string Name { get; init; }
    public string? Email { get; init; }
}

Nothing fancy here, an immutable record with two properties: Name and Email. At first glance, it may look fine, but the compiler won't like this code:

Program.cs(14, 19): [CS8618] Non-nullable property 'Name' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

This means that we created an immutable record with a non-nullable property Name, but nothing will stop us from creating a new user and not providing any name at all:

var user = new User { Email = "Sam" };

โŒ Solution 1: Assign default!

(Don't do that) Often, to shut the compiler up, programmers assign default values with an exclamation mark to such properties:

public record User
{
    public string Name { get; init; } = default!; // or null! or "" 
    public string? Email { get; init; }
}

Here we simply ask the compiler to ignore the issue, while the problem remains: we are still able to create an instance of that record and forget to assign a value to Name.

๐Ÿ’ฉ Solution 1: Constructor

public record User
{
    public string Name { get; }
    public string? Email { get; init; }

    public User(string name, string? email = default)
    {
        Name = name;
        Email = email;
    }
}

Very boring and verbose code, which at the end of the day does the job. There is no way a developer can forget to pass a value for Namebecause now it's a mandatory constructor parameter.

โœ… Solution 2: Positional properties

public record User(string Name, string? Email = default);

In this case whenever we create a new instance of the User type, we have to provide a value for the Name property. Nice and clean.

โœ… Solution 3: required modifier

The new keyword required was introduced in C# 11, which can be applied to properties of classes, records and structs. You can find all details as well as information about existing limitations here.

public record User
{
    public required string Name { get; init; }
    public string? Email { get; init; }
}

Problem solved.

I hope it helps and thank you for reading!

Cheers!

Did you find this article valuable?

Support Aleksei Zagoskin by becoming a sponsor. Any amount is appreciated!

ย