假设我有以下课程
public class MyClass {
private readonly NinjaObject _myNinja;
MyClass(NinjaFactory ninjaFactory) {
_myNinja = ninjaFactory.CreateNinjaButItTakesTime();
}
public void NinjaAttack() {
_myNinja.Attack();
}
}
现在,构造函数应该做所有事情来初始化类并准备好供应用程序使用,即创建 Ninja 并准备好在调用时进行攻击。让构造函数快速执行其操作也是一个好主意。然而,如果创建忍者的过程需要很长时间,这可能是不可能的。我认为您可以通过某种静态工厂方法异步创建 Ninja,但是当应用程序调用时,您会面临 Ninja 未准备好(即空对象)进行攻击的风险(至少理论上)。
当类的字段对于该类的操作至关重要但可能需要很长时间才能构建时,创建它们时采用的最佳模式是什么?在构造函数中保持同步,还是应用某种异步模式?
我认为您可以通过某种静态工厂方法异步创建 Ninja,但是当应用程序调用时,您会面临 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();
}
您无法避免花费很长时间 - 但您可以通过将构造函数调用留到最后来使创建异步。这基本上解决了构造函数不能异步的限制。
另一种选择 - 但很危险 - 是开始创建忍者的任务并将 that 传递到构造函数中,但稍后使用任务的结果:
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)
。
如果 Ninja 工厂方法的异步版本确实可用,Jon 接受的答案就可以正常工作。如果没有,并且您仍然希望构造函数快速返回,您可以延迟初始化 Ninja:
public class MyClass
{
private readonly Lazy<NinjaObject> _myNinja;
public MyClass(NinjaFactory ninjaFactory)
{
_myNinja = new(ninjaFactory.CreateNinjaButItTakesTime);
}
public void NinjaAttack() => _myNinja.Value.Attack();
}
但是,有一个警告:由于初始化延迟,第一次攻击可能会慢得令人无法接受。