Long running initialisation of class fields in constructor

Say I have the class below

public class MyClass {
    private readonly NinjaObject _myNinja;

    MyClass(NinjaFactory ninjaFactory) {
        _myNinja = ninjaFactory.CreateNinjaButItTakesTime();
    }

    public void NinjaAttack() {
        _myNinja.Attack();
    }
}

Now, the constructor should do everything to initialise the class and get it ready for use by the application, i.e., create the Ninja and have it ready to attack when called to do so. It's also a good idea to have your constructor perform their actions quickly. However, this may not be possible if the process of creating a Ninja takes a long time. I presume you could create the Ninja asynchronously through some kind of static factory method, but then you run the risk (at least theoretically) of the Ninja not being ready (i.e. a null object) to attack when called to do so by the application.

When you have fields of a class that are critical to how that class operates but can take a long time to construct, what is the best pattern to adopt in creating them? Keep it synchronous in the constructor, or apply some kind of asynchronous pattern?

Jon Skeet
people
quotationmark

I presume you could create the Ninja asynchronously through some kind of static factory method, but then you run the risk (at least theoretically) of the Ninja not being ready (i.e. a null object) to attack when called to do so by the application.

Well, this is where an async static factory method comes in handy:

public class MyClass
{
    private readonly Ninja ninja;

    private MyClass(Ninja ninja)
    {
        this.ninja = ninja;
    }

    public static async Task<MyClass> Create()
    {
        // Or however you create a ninja - ideally properly asynchronous
        var ninja = await Ninja.Create();
        return new MyClass(ninja);
    }

    public void NinjaAttack() => ninja.Attack();
}

You can't avoid it taking a long time - but you can make the creation asynchronous by leaving the constructor call right to the end. This basically works around the restriction that constructors can't be asynchronous.

An alternative - but a dangerous one - would be to start the task creating the ninja and pass that into the constructor, but use the result of the task later:

public class MyClass
{
    private readonly Task<Ninja> ninjaTask;

    public MyClass(Task<Ninja> ninjaTask)
    {
        this.ninjaTask = ninjaTask;
    }

    public void NinjaAttack() => ninjaTask.Result.Attack();
}

That's dangerous because using Task<T>.Result can deadlock in some cases, if the task needs to do more work in the current synchronization context before completing. You could avoid that by making your NinjaAttack method asynchronous instead:

public class MyClass
{
    private readonly Task<Ninja> ninjaTask;

    public MyClass(Task<Ninja> ninjaTask)
    {
        this.ninjaTask = ninjaTask;
    }

    public async Task NinjaAttack() => (await ninjaTask).Attack();
}

Depending on your context, you might want to use await ninjaTask.ConfigureAwait(false).

people

See more on this question at Stackoverflow