Подтвердить что ты не робот

Преобразование выражения async lambda для делегирования типа System.Func <T>?

У меня есть метод async внутри переносимой библиотеки классов с этой сигнатурой:

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

Он извлекает ресурс, который отбрасывается как конкретный тип T.

Я работаю с библиотекой сторонних кешей (Akavache), которая требует Func<T> как одного из параметров и имеет попытался сделать это следующим образом:

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

Это приводит к ошибке:

Невозможно преобразовать выражение async lambda для делегирования типа 'System.Func<T>'. Асинхронное лямбда-выражение может возвращать void, Task или Task<T>, ни одна из которых не конвертируется в 'System.Func<T>'.

Я пробовал различные перестановки Func<T> присваивания без всякой удачи, единственный способ заставить код работать - сделать блокировку Func<T>:

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

который блокирует мое приложение.

Любые указатели на то, где я сбиваюсь с пути?

4b9b3361

Ответ 1

Нет. Когда кто-то ожидает Func<T> f, вы можете предположить, что он будет вызван с чем-то вроде result = f() - то есть он не знает об асинхронном поведении. Если вы обманываете его, используя .Result, как у вас, он будет тупик в потоке пользовательского интерфейса, потому что он хочет запланировать код после await (в _Fetch) в потоке пользовательского интерфейса, но вы уже заблокировали его с помощью .Result.

Асинхронная лямбда может быть передана в Action, так как она не имеет возвращаемого значения - или Func<Task> или Func<Task<T>>.

Посмотрев на ваш случай, GetOrCreateObject, похоже, вызывает GetOrFetchObject. Одна из перегрузок GetOrFetchObject принимает значение Func<Task<T>>. Вы можете попробовать вызвать этот метод с помощью асинхронной лямбда и посмотреть, помогает ли она.

Ответ 2

Ответ YK1 объясняет, почему вы не можете рассматривать Func<T> как асинхронный.

Чтобы устранить проблему, используйте GetOrFetchObject вместо GetOrCreateObject. Методы "create" предполагают (синхронное) создание, тогда как методы "выборки" работают с (асинхронным) извлечением.

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

Я также удалил ненужный async/await в вашем лямбда-выражении. Поскольку _Fetch уже возвращает Task<T>, нет необходимости создавать async лямбда, единственной целью которой является await эта задача.

Ответ 3

Что-то вроде этого?

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