如何使用异步等待模式初始化对象 [英] How to initialize an object using async-await pattern

查看:19
本文介绍了如何使用异步等待模式初始化对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在我的服务类中遵循 RAII 模式,这意味着当一个对象被构造时,它被完全初始化.但是,我在使用异步 API 时遇到了困难.有问题的类的结构如下

I'm trying to follow RAII pattern in my service classes, meaning that when an object is constructed, it is fully initialized. However, I'm facing difficulties with asynchronous APIs. The structure of class in question looks like following

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
    }
}

我的目标还在于消除 ImportantValue getter 中的副作用,使其成为线程安全的.

I'm also targeting to get rid of side-effects in ImportantValue getter, to make it thread-safe.

现在ServiceProvider的用户将创建它的一个实例,订阅ImportantValue变化的事件,并获得初始的ImportantValue.问题来了,初始值.由于 ImportantValue 是异步计算的,因此无法在构造函数中完全初始化该类.最初将此值设为 null 可能没问题,但随后我需要在某个地方首次计算它.一个自然的地方可能是 ImportantValue 的 getter,但我的目标是使其线程安全且没有副作用.

Now users of ServiceProvider will create an instance of it, subscribe to an event of ImportantValue change, and get the initial ImportantValue. And here comes the problem, with the initial value. Since the ImportantValue is calculated asynchronously, the class cannot be fully initialized in constructor. It may be okay to have this value as null initially, but then I need to have some place where it will be calculated first time. A natural place for that could be the ImportantValue's getter, but I'm targeting to make it thread-safe and with no side-effects.

所以我基本上被这些矛盾所困.你能帮我提供一些替代方案吗?在构造函数中初始化值虽然 nice 并不是真正必要的,但没有副作用和属性的线程安全是强制性的.

So I'm basically stuck with these contradictions. Could you please help me and offer some alternative? Having value initialized in constructor while nice is not really necessary, but no side-effects and thread-safety of property is mandatory.

提前致谢.

还要添加一件事.我正在使用 Ninject 进行实例化,据我所知,它不支持异步方法来创建绑定.虽然在构造函数中启动一些基于任务的操作的方法可行,但我不能等待它的结果.

One more thing to add. I'm using Ninject for instantiation, and as far as I understand, it doesn't support async methods to create a binding. While approach with initiating some Task-based operation in constructor will work, I cannot await its result.

即接下来的两种方法(目前作为答案提供)将无法编译,因为返回的是 Task,而不是我的对象:

I.e. two next approaches (offered as answers so far) will not compile, since Task is returned, not my object:

Kernel.Bind<IServiceProvider>().ToMethod(async ctx => await ServiceProvider.CreateAsync())

Kernel.Bind<IServiceProvider>().ToMethod(async ctx => 
{
    var sp = new ServiceProvider();
    await sp.InitializeAsync();
})

简单绑定可以工作,但我不会等待在构造函数中启动的异步初始化的结果,正如 Stephen Cleary 提出的:

Simple binding will work, but I'm not awaiting the result of asynchronous initialization started in constructor, as proposed by Stephen Cleary:

Kernel.Bind<IServiceProvider>().To<ServiceProvider>();

...这对我来说不太好.

... and that's not looking good for me.

推荐答案

我有一篇博客文章描述了 async 构建的几种方法.

I have a blog post that describes several approaches to async construction.

我推荐 Reed 描述的异步工厂方法,但有时这是不可能的(例如,依赖注入).在这些情况下,您可以使用这样的异步初始化模式:

I recommend the asynchronous factory method as described by Reed, but sometimes that's not possible (e.g., dependency injection). In these cases, you can use an asynchronous initialization pattern like this:

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

然后您可以正常构造类型,但请记住,构造仅开始异步初始化.当您需要初始化类型时,您的代码可以:

You can then construct the type normally, but keep in mind that construction only starts the asynchronous initialization. When you need the type to be initialized, your code can do:

await myTypeInstance.Initialization;

请注意,如果 Initialization 已经完成,执行(同步)会在 await 之后继续执行.

Note that if Initialization is already complete, execution (synchronously) continues past the await.

如果你确实想要一个实际的 异步属性,我有一篇关于它的博客文章,你的情况听起来可能会从 AsyncLazy 中受益:

If you do want an actual asynchronous property, I have a blog post for that, too. Your situation sounds like it may benefit from 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; }
}

这篇关于如何使用异步等待模式初始化对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆