Has*_*tor 16 c# design-patterns ninject raii async-await
我试图在我的服务类中遵循RAII模式,这意味着当构造一个对象时,它会被完全初始化.但是,我遇到了异步API的困难.有问题的类的结构如下所示
class ServiceProvider : IServiceProvider // Is only used through this interface
{
public int ImportantValue { get; set; }
public event EventHandler ImportantValueUpdated;
public ServiceProvider(IDependency1 dep1, IDependency2 dep2)
{
// IDependency1 provide an input value to calculate ImportantValue
// IDependency2 provide an async algorithm to calculate ImportantValue
}
}
Run Code Online (Sandbox Code Playgroud)
我也打算在ImportantValuegetter中摆脱副作用,使其成为线程安全的.
现在,用户ServiceProvider将创建它的实例,订阅ImportantValue更改事件,并获得初始值ImportantValue.这里出现了问题,初始值.由于ImportantValue是异步计算的,因此无法在构造函数中完全初始化类.最初将此值设为null可能没问题,但是我需要在某个地方首次计算它.一个自然的地方可能是ImportantValue吸气剂,但我的目标是使其线程安全,没有副作用.
所以我基本上坚持这些矛盾.你能帮助我并提供一些替代方案吗?在构造函数中初始化值虽然不是很好,但是没有副作用和属性的线程安全是必需的.
提前致谢.
编辑:还有一件事要补充.我正在使用Ninject进行实例化,据我所知,它不支持异步方法来创建绑定.虽然在构造函数中启动一些基于任务的操作的方法将起作用,但我无法等待其结果.
即两个下一个方法(目前为止提供的答案)将无法编译,因为返回了Task,而不是我的对象:
Kernel.Bind<IServiceProvider>().ToMethod(async ctx => await ServiceProvider.CreateAsync())
Run Code Online (Sandbox Code Playgroud)
要么
Kernel.Bind<IServiceProvider>().ToMethod(async ctx =>
{
var sp = new ServiceProvider();
await sp.InitializeAsync();
})
Run Code Online (Sandbox Code Playgroud)
简单绑定将起作用,但我不是在等待构造函数中启动的异步初始化的结果,正如Stephen Cleary所提出的:
Kernel.Bind<IServiceProvider>().To<ServiceProvider>();
Run Code Online (Sandbox Code Playgroud)
......这对我来说并不好看.
Ste*_*ary 39
我有一篇博客文章介绍了几种async构建方法.
我推荐Reed所描述的异步工厂方法,但有时这是不可能的(例如,依赖注入).在这些情况下,您可以使用如下的异步初始化模式:
public sealed class MyType
{
public MyType()
{
Initialization = InitializeAsync();
}
public Task Initialization { get; private set; }
private async Task InitializeAsync()
{
// Asynchronously initialize this instance.
await Task.Delay(100);
}
}
Run Code Online (Sandbox Code Playgroud)
然后,您可以正常构造类型,但请记住,构造仅启动异步初始化.当您需要初始化类型时,您的代码可以执行以下操作:
await myTypeInstance.Initialization;
Run Code Online (Sandbox Code Playgroud)
请注意,如果Initialization已经完成,则执行(同步)继续经过await.
如果你确实想要一个实际的异步属性,我也有一个博客文章.您的情况听起来可能会受益于AsyncLazy<T>:
public sealed class MyClass
{
public MyClass()
{
MyProperty = new AsyncLazy<int>(async () =>
{
await Task.Delay(100);
return 13;
});
}
public AsyncLazy<int> MyProperty { get; private set; }
}
Run Code Online (Sandbox Code Playgroud)
一种可能的选择是将其移动到工厂方法而不是使用构造函数。
然后您的工厂方法可以返回 a Task<ServiceProvider>,这将允许您异步执行初始化,但在(异步)计算ServiceProvider之前不会返回构造的ImportantValue。
这将允许您的用户编写如下代码:
var sp = await ServiceProvider.CreateAsync();
int iv = sp.ImportantValue; // Will be initialized at this point
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
16879 次 |
| 最近记录: |