C#中的等待属性

时间:2017-05-30 16:50:38

标签: c# properties async-await

在C#中,是否违反最佳做法才能拥有Task类型的属性,这样可以await属性?

我确认C#编译器允许使用此代码,但我很好奇这是否违反了最佳做法?

下面是一个示例,说明如何实现awaitable属性来检索SQLite数据库。

示例代码

在父类中消耗等待的财产

public class Database : BaseDatabase
{
    ...

    async Task<List<MyModel>> GetAllData()
    {
        //**** Awaiting a Property ****//
        var databaseConnection = await DatabaseConnectionTask;

        databaseConnection.Table<MyModel>().ToListAsync();
    }

    ...
}

在基类中创建等待的属性

public abstract class BaseDatabase
{
    readonly SQLiteAsyncConnection _databaseConnection = new SQLiteAsyncConnection(path);

    bool _isInitialized;

    //**** Awaitable Property ****//
    protected Task<SQLiteAsyncConnection> DatabaseConnectionTask => GetDatabaseConnection();

    static async Task<SQLiteAsyncConnection> GetDatabaseConnection()
    {
        if (!_isInitialized)
            await Initialize();

        return _databaseConnection;
    }

    static async Task Initialize()
    {
        await _databaseConnection.CreateTableAsync<MyModel>();
        _isInitialized = true;
    }
}

1 个答案:

答案 0 :(得分:2)

我有blog post on the subject

总之,启动新的异步操作的属性可能不是一个好的设计。它们本质上只是一种更笨拙的语法中的异步方法。

您的当前代码每次调用其get访问器时都会启动一个新的异步操作。但是,根据用途,我预计这是一个错误。你可能想要一个只读属性:

protected Task<SQLiteAsyncConnection> DatabaseConnectionTask { get; }
    = GetDatabaseConnection();

将共享读取该属性的所有方法中的任务(和连接)。

只读Task属性比异步getter更好,但仍有一些注意事项。例如,GetDatabaseConnection在构造函数中启动。如果不需要,那么AsyncLazy<T>会更合适。

此外,只要您“缓存”Task这样的实例(无论是否使用AsyncLazy<T>),您都必须特别注意错误处理。例如,如果连接失败,则缓存的任务将缓存该异常。您应该考虑在工厂方法中使用重试(例如,Polly),和/或使用AsyncLazyFlags.RetryOnFailure之类的内容(如果使用我的AsyncLazy<T>类型)。