如何使用async / await链接.net中的方法

时间:2018-10-10 09:50:12

标签: c# .net async-await method-chaining

我已经开始学习函数式编程,虽然在通常情况下链接方法看起来很棒(我认为),但是在处理异步/等待时确实变得很丑

C++

有什么方法可以消除这种噪音?

编辑:

await (await (await CosmosDbRepository<ApplicationProcess>
    .GetItemAsync(param.ProcessId))
.Historize(() => _analyseFinanciereService.ProcessAsync(), 
    ProcessStepEnum.Application))
.Notify(p => p.GetLastStep());

Edit2:带有接受Task的扩展方法

public static async Task<ApplicationProcess> Historize(
this ApplicationProcess process, 
Func<Task> fn, 
ProcessStepEnum stepEnum)
{
    var dateStart = DateTime.UtcNow;
    var error = string.Empty;
    try
    {
        await fn();
        return process;
    }
    …

public static async Task Notify<TResult>(
    this ApplicationProcess process, 
    Func<ApplicationProcess, TResult> fn)
...

这就是我一直在寻找的内容,即使我对最新评论感到困惑

2 个答案:

答案 0 :(得分:2)

我以LinqPad query上传的所有显示的代码,因此您可以立即尝试。

函数式编程的概念为monad(我强烈建议陌生的C#程序员从提供的链接开始)。 C#任务可能被认为是monad,据我了解,这正是您所需要的。

出于这个答案的目的,我简化了您所拥有的示例:

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#具有用于处理monad的不错的语法: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关键字之后定义的值的表达式。

答案 1 :(得分:0)

我试图以一种方式实现它-只有当源任务成功完成(没有错误)时,它才应该继续执行下一个任务。这似乎工作正常,但尝试思考在任何情况下都可能失败:-

return await await sourceTask.ContinueWith(async st => 
            {
               var res = await st; //
               return await contWith(res);
            }, TaskContinuationOptions.NotOnFaulted & TaskContinuationOptions.OnlyOnRanToCompletion;