Skip to content
Carmel Eve By Carmel Eve Software Engineer I
How to fully initialize types in their constructor with C# nullable using the async factory pattern

Here at endjin, we've recently enabled the new nullable references features in all of our projects. This has led me to a realisation that I often don't think as much as I should about object initialisation patterns. A couple of weeks ago I ran into an issue where I was trying to initalise an object, but the initialisation involved asynchronous operations so couldn't be done in a constructor.

When I first created the class I did this:

public class Thing
{
    public string AString { get; private set; }
    
    public static async Task<Thing> CreateThingAsync()
    {
        return new Thing()
        {
            AString = await GetStringAsync()
        };
    }
}

Where the asynchronous inititialisation is done via a factory method. However, this means that a set function is required for the property, and also, when nullable references are enabled you get this warning:

Warning message saying "Non-nullable property 'AString' is uninitialized. Consider declaring property as nullable.

This is because the AString property is not initialised in the constructor and is therefore technically able to be null. It would be better to be able to make the property truly readonly, and to ensure that it is always initialised as non-null.

To do this we can use the async factory method pattern:

public class Thing
{
    private Thing(string aString)
    {
        this.AString = aString;
    }

    public string AString { get; }
    
    public static async Task<Thing> CreateThingAsync()
    {
        var aString = await GetStringAsync();
        return new Thing(aString);
    }
}

Here we have a constructor which takes a string, which we use in the initialiser to produce a fully populated object. Also, as the constructor is private we know that the objects can only be initalisated from inside the Thing class. And, as the constructor takes a non-nullable string, we will get a warning if we try and pass anything that could be null in.

For example, if the GetStringAsync method returned a nullable string:

public static async Task<string?> GetStringAsync()
{
    await Task.Delay(1).ConfigureAwait(false);

    return null;
}

Then we would see this warning from the compiler when we try to pass it into the consructor:

Warning message saying "possible null reference assignment"

This is a simple pattern but one that I think is incredibly useful in avoiding partial instantiation and null reference exceptions!

FAQs

How do you ensure a C# non-nullable property is always initialized? Use the factory method pattern as shown here.
How do you create readonly objects in C# with async initialisation? Use the async factory factory method pattern as shown here.

Carmel Eve

Software Engineer I

Carmel Eve

Carmel is a software engineer, LinkedIn Learning instructor and STEM ambassador.

Over the past four years she has been focused on delivering cloud-first solutions to a variety of problems. These have ranged from highly-performant serverless architectures, to web applications, to reporting and insight pipelines and data analytics engines.

In her time at endjin, she has written many blog posts covering a huge range of topics, including deconstructing Rx operators and mental well-being and managing remote working.

Carmel's first LinkedIn Learning course on how to prepare for the Az-204 exam - developing solutions for Microsoft Azure - was released in April 2021. Over the last couple of years she has also spoken at NDC, APISpecs and SQLBits. These talks covered a range of topics, from reactive big-data processing to secure Azure architectures.

She is also passionate about diversity and inclusivity in tech. She is a STEM ambassador in her local community and is taking part in a local mentorship scheme. Through this work she hopes to be a part of positive change in the industry.

Carmel won "Apprentice Engineer of the Year" at the Computing Rising Star Awards 2019.

Carmel worked at endjin from 2016 to 2021.