在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;
}
}
答案 0 :(得分:2)
总之,启动新的异步操作的属性可能不是一个好的设计。它们本质上只是一种更笨拙的语法中的异步方法。
您的当前代码每次调用其get
访问器时都会启动一个新的异步操作。但是,根据用途,我预计这是一个错误。你可能想要一个只读属性:
protected Task<SQLiteAsyncConnection> DatabaseConnectionTask { get; }
= GetDatabaseConnection();
将共享读取该属性的所有方法中的任务(和连接)。
只读Task
属性比异步getter更好,但仍有一些注意事项。例如,GetDatabaseConnection
在构造函数中启动。如果不需要,那么AsyncLazy<T>
会更合适。
此外,只要您“缓存”Task
这样的实例(无论是否使用AsyncLazy<T>
),您都必须特别注意错误处理。例如,如果连接失败,则缓存的任务将缓存该异常。您应该考虑在工厂方法中使用重试(例如,Polly),和/或使用AsyncLazyFlags.RetryOnFailure
之类的内容(如果使用我的AsyncLazy<T>
类型)。