将async lambda表达式转换为委托类型System.Func <t>?</t>

时间:2013-06-21 01:16:45

标签: c# async-await c#-5.0 portable-class-library

我在具有此签名的可移植类库中有一个异步方法:

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表达式可能会返回voidTaskTask<T>,其中任何一个都无法转换为“System.Func<T>”。

我尝试了Func<T>任务的各种排列而没有任何运气,我可以让代码工作的唯一方法是使Func<T>阻止:

await this.CacheProvider.GetOrCreateObject<T>(key, 
    () => _Fetch<T>(uri).Result, cacheExpiry); 

使我的应用程序死锁。

关于我误入歧途的任何指示?

3 个答案:

答案 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;
 }