在构造函数

时间:2017-03-26 08:39:08

标签: c# asynchronous constructor synchronous

说我有下面的课程

public class MyClass {
    private readonly NinjaObject _myNinja;

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

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

现在,构造函数应该做所有事情来初始化类并让它准备好供应用程序使用,即创建Ninja并准备好在被调用时进行攻击。让构造函数快速执行操作也是一个好主意。但是,如果创建忍者的过程需要很长时间,这可能是不可能的。我认为你可以通过某种静态工厂方法异步创建Ninja,但是当你被应用程序调用时,你会冒险(至少在理论上)让Ninja没有准备好(即一个空对象)进行攻击。

如果某个类的字段对于该类的操作方式至关重要但可能需要很长时间才能构建,那么创建它们时采用的最佳模式是什么?在构造函数中保持同步,还是应用某种异步模式?

1 个答案:

答案 0 :(得分:3)

  

我认为你可以通过某种静态工厂方法异步创建Ninja,但是当你通过某种静态工厂方法调用时,你会冒险(至少在理论上)没有准备好(即一个空对象)攻击申请。

嗯,这就是异步静态工厂方法派上用场的地方:

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();
}

您无法避免花费很长时间 - 但是可以通过将构造函数调用保持在最后来使创建异步。这基本上围绕着构造函数不能异步的限制。

另一种选择 - 但是一种危险的选择 - 是启动创建忍者的任务并将 传递给构造函数,但稍后使用该任务的结果:

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

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

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

这很危险,因为如果任务需要在完成之前在当前同步上下文中执行更多工作,则在某些情况下使用Task<T>.Result可能会死锁。您可以通过异步使NinjaAttack方法异步来避免这种情况:

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

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

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

根据您的具体情况,您可能希望使用await ninjaTask.ConfigureAwait(false)