web-dev-qa-db-fra.com

Convertir l'expression lambda asynchrone en type délégué System.Func <T>?

J'ai une méthode asynchrone dans une bibliothèque de classes portable avec cette signature:

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

Il récupère une ressource qui est rejetée en tant que type concret T.

Je travaille avec une bibliothèque de cache tierce ( Akavache ) qui nécessite un Func<T> comme l'un des paramètres et a essayé de le faire de cette manière:

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

Cela se traduit par l'erreur:

Impossible de convertir l'expression lambda asynchrone en type de délégué 'System.Func<T> '. Une expression lambda asynchrone peut renvoyer void, Task ou Task<T>, dont aucun n'est convertible en 'System.Func<T> '.

J'ai essayé différentes permutations de Func<T> affectation sans aucune chance, la seule façon de faire fonctionner le code est de faire le Func<T> blocage:

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

qui bloque mon application.

Des conseils sur où je m'égare?

28

Ne peut faire. Lorsque quelqu'un attend un Func<T> f, Vous pouvez supposer qu'il sera invoqué avec quelque chose comme result = f() - c'est-à-dire qu'il ne connaît pas le comportement asynchrone. Si vous le trichez en utilisant .Result Comme vous l'avez fait - il se bloquera sur le thread d'interface utilisateur car il veut planifier le code après await (dans _Fetch) sur le thread d'interface utilisateur, mais vous avez déjà bloqué avec .Result.

Lambda asynchrone peut être passé à Action car il n'a pas de valeur de retour - ou à Func<Task> Ou Func<Task<T>>.

En regardant votre cas, le GetOrCreateObject semble appeler GetOrFetchObject. L'une des surcharges GetOrFetchObject accepte un Func<Task<T>>. Vous pouvez essayer d'appeler cette méthode avec votre lambda asynchrone et voir si cela aide.

18
YK1

réponse de YK1 explique pourquoi vous ne pouvez pas traiter Func<T> asynchrone.

Pour résoudre votre problème, utilisez GetOrFetchObject au lieu de GetOrCreateObject. Les méthodes "create" supposent une création (synchrone), tandis que les méthodes "fetch" fonctionnent avec la récupération (asynchrone).

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

J'ai également supprimé le async/await inutile dans votre expression lambda. Puisque _Fetch renvoie déjà Task<T>, il n'est pas nécessaire de créer un async lambda dont le seul but est de await cette tâche.

6
Stephen Cleary