简体   繁体   中英

Convert async lambda expression to delegate type System.Func<T>?

I have an async method inside a portable class library with this signature:

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

It fetches a resource that is cast back as a concrete type T.

I'm working with a 3rd party cache library ( Akavache ) that requires a Func<T> as one of the parameters and have tried to do so in this manner:

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

This results in the error:

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> '.

I've tried various permutations of Func<T> assignment without any luck, the only way I can get the code to work is to make the Func<T> blocking:

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

which deadlocks my app.

Any pointers on where I'm going astray?

No can do. When someone expects a Func<T> f you can assume it will be invoked with something like result = f() - ie, it does not know about async behavior. If you cheat it by using .Result like you have - it will deadlock on UI thread because it wants to schedule the code after await (in _Fetch) on the UI thread, but you have already blocked it with .Result .

Async lambda can be passed to Action since it has no return value - or to Func<Task> or Func<Task<T>> .

Looking at your case, the GetOrCreateObject appears to be calling GetOrFetchObject . One of the GetOrFetchObject overloads accepts a Func<Task<T>> . You can try calling that method with your async lambda and see if it helps.

YK1's answer explains why you can't treat Func<T> as asynchronous.

To fix your problem, use GetOrFetchObject instead of GetOrCreateObject . The "create" methods assume a (synchronous) creation, while the "fetch" methods work with (asynchronous) retrieval.

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

I also removed the unnecessary async / await in your lambda expression. Since _Fetch already returns Task<T> , there's no need to create an async lambda whose sole purpose is to await that task.

Something like this?

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM