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

Cra*_*SFT 27 c# async-await c#-5.0 portable-class-library

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

private async Task<T> _Fetch<T>(Uri uri)
Run Code Online (Sandbox Code Playgroud)

它获取一个被回送为具体类型T的资源.

我正在使用第三方缓存库(Akavache),它需要一个Func<T>参数作为其中一个,并试图以这种方式这样做:

await this.CacheProvider.GetOrCreateObject<T>(key,
    async () => await _Fetch<T>(uri), cacheExpiry);
Run Code Online (Sandbox Code Playgroud)

这会导致错误:

无法将异步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); 
Run Code Online (Sandbox Code Playgroud)

哪个死了我的应用程序.

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

YK1*_*YK1 20

没有办法.当有人期望Func<T> f你可以假设它会被调用类似的东西 result = f()- 也就是说,它不知道异步行为.如果你.Result像你一样使用它来欺骗它- 它将在UI线程上死锁,因为它想要在UI线程之后await(在_Fetch中)安排代码,但是你已经用它阻止了它.Result.

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

看着你的情况,GetOrCreateObject似乎在呼唤GetOrFetchObject.其中一个GetOrFetchObject重载接受a Func<Task<T>>.您可以尝试使用异步lambda调用该方法,看看它是否有帮助.

  • 我想强调的是,如果一个方法接受一个`Action`并且你传递一个`async` lambda,那么大多数时候这是错误的. (3认同)

Ste*_*ary 6

YK1的答案解释了为什么你不能将其Func<T>视为异步.

要解决您的问题,请使用GetOrFetchObject而不是GetOrCreateObject."create"方法假定(同步)创建,而"fetch"方法使用(异步)检索.

await CacheProvider.GetOrFetchObject<T>(key, () => _Fetch<T>(uri), cacheExpiry)
Run Code Online (Sandbox Code Playgroud)

我还删除了lambda表达式中不必要的async/ await.由于_Fetch已经返回Task<T>,因此不需要创建一个asynclambda,其唯一目的是执行await该任务.