2013-06-21 49 views
18

我有一个便携式类库中的异步方法与此签名:将异步lambda表达式转换为委托类型System.Func <T>?

private async Task<T> _Fetch<T>(Uri uri) 

它获取被转换回为一个具体类型T.

我与第三方缓存工作的资源库(Akavache),需要一个Func<T>作为参数之一,曾尝试在这种方式这样做:

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

这导致错误:

Cannot convert async lambda expression to delegate type ' System.Func<T> '. An async lambda expression may return void , Task or Task<T> , none of which are convertible to ' System.Func<T> '.

我试过Func<T>分配的各种排列没有任何的运气,我可以得到代码工作的唯一方法是使Func<T>阻塞:

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

这会造成死锁我的应用程序。

任何指向哪里我会误入歧途?

+0

你在找'Func ' –

回答

13

不行。当有人预计Func<T> f时,你可以认为它会被调用类似result = f()的东西 - 即它不知道异步行为。如果您像使用.Result一样欺骗它 - 它将在UI线程上死锁,因为它想要在UI线程中的await(in _Fetch)之后计划代码,但是您已经用.Result阻止了它。

异步lambda可以传递给Action,因为它没有返回值 - 或Func<Task>Func<Task<T>>

看着你的情况,GetOrCreateObject看起来叫GetOrFetchObject。其中一个GetOrFetchObject过载接受Func<Task<T>>。你可以尝试用你的异步lambda来调用这个方法,看看它是否有帮助。

+3

我想强调一下,如果一个方法接受一个'Action'并且你传递了一个'async' lambda,那么大部分时间这就是错误的做法。 – svick

+0

@svick:是的,与你同意。就像接受'Func '的方法一样,接受'Action'的方法不会知道它的异步行为。不返回任何结果的异步lambda将理想地传递给'Func '。它仅仅是C#编译器允许它传递给'Action',因为没有返回值。 – YK1

+0

谢谢YK1,我现在觉得很傻 - 我已经实现了一个接口来抽象出缓存提供者,并错过了基础库中的替代方法。 –

-3

这样的事情?

Public Func<T> ConvertTask<T>(Task<T> task) 
{ 
    return()=>task.Result; 
} 
+0

这个问题清楚地表明,使用'Result'会导致死锁。 – svick

4

YK1's answer解释了为什么你不能把Func<T>为异步。

要解决您的问题,请使用GetOrFetchObject而不是GetOrCreateObject。 “创建”方法假定(同步)创建,而“获取”方法与(异步)检索一起工作。

await CacheProvider.GetOrFetchObject<T>(key,() => _Fetch<T>(uri), cacheExpiry) 

我还你lambda表达式除去不必要async/await。由于_Fetch已经返回Task<T>,因此不需要创建async lambda,其唯一目的是await该任务。

+0

谢谢斯蒂芬,这正是我最终解决问题的方法。 –

相关问题