我在具有此签名的可移植类库中有一个异步方法:
private async Task<T> _Fetch<T>(Uri uri)
它获取一个被转换为具体类型T的资源。
我正在使用第三方缓存库(Akavache),该库需要Func<T>
作为其中一个参数,并尝试以这种方式执行此操作:
await this.CacheProvider.GetOrCreateObject<T>(key,
async () => await _Fetch<T>(uri), cacheExpiry);
这会导致错误:
无法将异步lambda表达式转换为委托类型“
System.Func<T>
”。异步lambda表达式可能会返回void
,Task
或Task<T>
,其中任何一个都无法转换为“System.Func<T>
”。
我尝试了Func<T>
任务的各种排列而没有任何运气,我可以让代码工作的唯一方法是使Func<T>
阻止:
await this.CacheProvider.GetOrCreateObject<T>(key,
() => _Fetch<T>(uri).Result, cacheExpiry);
使我的应用程序死锁。
关于我误入歧途的任何指示?
答案 0 :(得分:18)
没有办法。当某人期望Func<T> f
时,您可以假设它将使用类似result = f()
的内容进行调用 - 即,它不知道异步行为。如果你像你一样使用.Result
作弊 - 它将在UI线程上死锁,因为它想在UI线程上await
(在_Fetch中)之后安排代码,但是你已经用它阻止了它.Result
。
异步lambda可以传递给Action
,因为它没有返回值 - 或Func<Task>
或Func<Task<T>>
。
查看您的案例时,GetOrCreateObject
似乎在调用GetOrFetchObject
。其中一个GetOrFetchObject
重载会接受Func<Task<T>>
。您可以尝试使用异步lambda调用该方法,看看它是否有帮助。
答案 1 :(得分:6)
YK1's answer解释了为什么您不能将Func<T>
视为异步。
要解决您的问题,请使用GetOrFetchObject
代替GetOrCreateObject
。 “create”方法假定(同步)创建,而“fetch”方法使用(异步)检索。
await CacheProvider.GetOrFetchObject<T>(key, () => _Fetch<T>(uri), cacheExpiry)
我还删除了lambda表达式中不必要的async
/ await
。由于_Fetch
已经返回Task<T>
,因此无需创建async
lambda,其唯一目的是await
该任务。
答案 2 :(得分:-5)
这样的东西?
Public Func<T> ConvertTask<T>(Task<T> task)
{
return ()=>task.Result;
}