Чтобы получить свободный синтаксис, вы можете создать функтор и монаду из Task<T>
с помощью следующих методов расширения:
public static async Task<TResult> Map<T, TResult>(this Task<T> source, Func<T, TResult> func)
{
var result = await source.ConfigureAwait(false);
return func(result);
}
public static async Task<TResult> Bind<T, TResult>(this Task<T> source, Func<T, Task<TResult>> func)
{
var result = await source.ConfigureAwait(false);
return await func(result).ConfigureAwait(false);
}
Это позволяет вам писать свой код в виде цепочки методов:
var resultTask = GetObjectAsync<IEnumerable<Foo>>()
.Map(foos => foos.First())
.Bind(foo => foo.SomeMethodAsync())
.Map(r => r.GetResult());
Конечно, я не рассматриваю производительность здесь, но для не критичной к производительности части кода я предпочитаю удобочитаемость микрооптимизациям, если это помогает сделать ваш код более понятным (в некоторых областях я нахожу несколько случаев, когдаэтот синтаксис помогает).
Также имейте в виду, что очевидно, что вы все еще получаете задание обратно, так что в какой-то момент вам придется либо ждать его, либо блокировать с помощью Result
или Wait
.
Объединение методов также может быть достигнуто с помощью ContinueWith
, как в ответе @Cory Nelson (тщательно используя правильные аргументы для планировщика и параметров), избегая создания асинхронного конечного автомата, но для простоты я использовал async и await в расширенияхчто совершенно безопасно.
Кроме того, если вы переименуете Map в Select и Bind to SelectMany (aи добавьте перегрузку с проекцией), вы также можете использовать синтаксис запросов LINQ для задач, хотя это, вероятно, вас не заинтересует, поскольку вы специально пытаетесь отойти от этого синтаксиса от того, что я могу прочитать.