Calling Async Methods In Constructors In C#
[C#, .NET]
Assume we have the following type, the Spy
.
public sealed class Spy
{
public string FirstName { get; }
public string Surname { get; }
public Spy(string firstName, string surname)
{
FirstName = firstName;
Surname = surname;
}
}
You can see here that it has a constructor that sets the FirstName
and Surname
.
Suppose we want to improve our Spy
and play a theme song during the construction of the object.
We first introduce the method:
// Play theme song here
public async Task PlaySong()
{
await Task.Delay(TimeSpan.FromSeconds(5));
Console.WriteLine("Playing that song!");
}
Then we call it from the constructor
.
public Spy(string firstName, string surname)
{
FirstName = firstName;
Surname = surname;
await PlaySong();
}
This code will fail to compile with the following errors:
AsyncConstructors failed with 1 error(s) (0.1s)
/Users/rad/Projects/blog/BlogCode/2025-05-29 - Async Construction/Spy.cs(12,13): error CS4033: The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
Build failed with 1 error(s) in 0.5s
You might try to make the constructor
async
but that is impossible - the constuctor
must return the object, and currently you cannot change the return type of the constructor.
There is, however, an elegant solution to this problem.
You can make a factory method that will be responsible for calling the constructor
and running any async
methods we may have.
public sealed class Spy
{
public string FirstName { get; }
public string Surname { get; }
private Spy(string firstName, string surname)
{
FirstName = firstName;
Surname = surname;
}
// Play theme song here
public async Task PlaySong()
{
await Task.Delay(TimeSpan.FromSeconds(5));
Console.WriteLine("Playing that song!");
}
/// <summary>
/// Factory method to return a spy
/// </summary>
/// <param name="firstName"></param>
/// <param name="surname"></param>
/// <returns></returns>
public static async Task<Spy> CreateSpy(string firstName, string surname)
{
// Call private constructor
var spy = new Spy(firstName, surname);
// Call async method
await spy.PlaySong();
return spy;
}
}
A couple of things to note:
- The factory method is static and async
- The constructor is private - and this cannot be called from outside.
- After calling the
constructor
, the factory method, itselfasync
, can call anotherasync
method.
This is relevant to our series on Designing, Building & Packaging A Scalable, Testable .NET Open Source Component, and in particular a problem we had when implementing the AmazonS3StorageEngine
where some of the Amazon libraries only had async versions.
We can this initialize a Spy
asynchronously as follows:
// Create the spy asynchronously
var jamesBond = await v2.Spy.CreateSpy("James", "Bond");
// Use the spy
Console.WriteLine($"Spy was created: {jamesBond.FirstName} {jamesBond.Surname}");
This will print the following:
Playing that song!
Spy was created: James Bond
TLDR
We can get around the fact that constructors
cannot be async
or call async
methods by implementing a factory method with a private constructor to initiailize our objects.
The code is in my GitHub.
Happy hacking!