Весь представленный код я загрузил в виде запроса LinqPad , так что вы можете попробовать его прямо сейчас.
Функциональное программирование имеет концепцию монады (незнакомые программисты на C # настоятельно рекомендуем начать с предоставленной ссылки).Задание AC # можно считать монадой, и, насколько я понимаю, это именно то, что Вам нужно.
Для целей этого ответа я сделал упрощенный пример того, что у Вас есть:
await (await (await A.GetNumber()).DoubleIt()).SquareIt()
где методы следующие (для моего удобства они определены как статические):
public static class A
{
public static Task<int> GetNumber(){return Task.FromResult(3);}
public static Task<int> DoubleIt(this int input){return Task.FromResult(2 * input);}
public static Task<int> SquareIt(this int input){return Task.FromResult(input * input);}
}
Теперь вы можете легко связать их цепочкой с помощью небольшого количества клея, который может выглядеть следующим образом:
public static async Task<TOut> AndThen<TIn, TOut>(this Task<TIn> inputTask, Func<TIn, Task<TOut>> mapping)
{
var input = await inputTask;
return (await mapping(input));
}
Метод AndThen
действует точно так же, как монадное связывание:
await
A.GetNumber()
.AndThen(A.DoubleIt)
.AndThen(A.SquareIt)
Что более важно, C # имеет хороший синтаксис для работы с монадами: синтаксис понимания запросов LINQ.Вам просто нужно определить метод SelectMany, который работает с желаемым вами типом (в данном случае Task), и вы готовы к работе.
Ниже я реализовал наиболее «хардкорную» перегрузку SelectMany (с дополнительными resultSelector
), что дает Вам наибольшую гибкость.Простая версия будет почти такой же, как AndThen
(я думаю, что переименование сделает эту работу).
public static async Task<TOut> SelectMany<TIn, TInterm, TOut>(
this Task<TIn> inputTask,
Func<TIn, Task<TInterm>> mapping,
Func<TIn, TInterm, TOut> resultSelector)
{
var input = await inputTask;
return resultSelector(input, await mapping(input));
}
С ним вы можете использовать синтаксис:
var task =
from num in A.GetNumber()
from doubled in num.DoubleIt()
from squared in num.SquareIt()
select $"number: {num} doubled: {doubled}, squared: {squared}";
Console.WriteLine(await task);
И вы получите number: 3 doubled: 6, squared: 9
.
Простая версия SelectMany позволит вам использовать squared
в качестве единственно возможного выражения в последней строке select
.Версия "hardcodre" позволяет использовать любое выражение, которое использует любое из значений, определенных после ключевого слова from
.