在c#中对异步Func <Task <T >>的3个调用之间有什么区别?

时间:2019-09-20 18:46:41

标签: c# async-await anonymous

方法WhatDifferences中的3个调用之间有什么区别?

这是测试代码:

async Task WhatDifferences(Context context)
{
    await ActionAsync(context, async x => await IsOddAsync(x).ConfigureAwait(false));
    await ActionAsync(context, x => IsOddAsync(x));
    await ActionAsync(context, IsOddAsync);
}

async Task<T> ActionAsync<T>(Context context, Func<Context, Task<T>> action)
{
    return await action(context).ConfigureAwait(false);
}

async Task<bool> IsOddAsync(Context context)
{
    return await Task.Run(() => context.Count++ % 2 == 1).ConfigureAwait(false);
}

class Context
{
    public int Count { get; set; }
}

我正在尝试确定要在我的代码库中使用哪个代码,并根据我的知识,这三个代码的行为相同。

问题与What's the method signature for passing an async delegate?

不同

如果我表现出更多的逻辑,您可能会知道我的担心

async Task<T> ActionAsync<T>(Context context, Func<Context, Task<T>> action)
{
    using (var transaction = new TransactionScope())
    {
        //do some async logic before action
        var result = await action(context).ConfigureAwait(false);
        //do other async validation logic after action
        return result;
    }
}

2 个答案:

答案 0 :(得分:3)

await与返回Task

两者之间的区别:

await ActionAsync(context, async x => await IsOddAsync(x).ConfigureAwait(false));
await ActionAsync(context, x => IsOddAsync(x));

在某些情况下,您不需要await(当然也不需要async

  

在以下情况下,执行异步操作的方法不需要使用await:

     
      
  • 方法内部只有一个异步调用
  •   
  • 异步调用位于方法的结尾
  •   
  • 没有必要在任务中发生捕获/处理异常
  •   

请参见Returning a Task without await

在这种情况下,您可以中间返回Task

请注意,行为上的差异很小-取决于IsOddAsync的实现方式:

  

重要提示:返回Task而不是等待它会更改方法的异常行为,因为它不会在启动任务的方法内而是在等待任务的方法内抛出异常。

正如加布里埃尔·卢西(Gabriel Luci)所指出的,在IsOddAsync的当前实现中(一次调用和一个await),行为上没有区别。

x => IsOddAsync(x)IsOddAsync

两者之间的区别

await ActionAsync(context, x => IsOddAsync(x));
await ActionAsync(context, IsOddAsync);

在第一个方法中,您将使用参数x创建一个匿名(lambda)方法。由于IsOddAsync还有一个参数(具有相同的类型),因此不需要lambda方法。

请注意,如果IsOddAsync具有其他参数(例如,和第二个参数,那么您需要lambda。示例:

await ActionAsync(context, x => IsOddAsync(x, "mySecondParameter"));

在这种情况下,除了在堆栈中引发异常时的调用堆栈之外,行为没有其他差异。

答案 1 :(得分:3)

  

我正在尝试确定要在我的代码库中使用哪个代码,并根据我的知识,这三个代码的行为相同。

在这种情况下,这实际上是正确的。

此人创建了一个引用IsOddAsync方法的委托:

await ActionAsync(context, IsOddAsync);

此代码为lambda表达式创建了一个方法,委托引用了该编译器生成的方法:

await ActionAsync(context, x => IsOddAsync(x));

此操作也相同,但对于异步lambda,因此编译器生成的方法还具有一个async状态机:

await ActionAsync(context, async x => await IsOddAsync(x).ConfigureAwait(false));

通常,您的问题可以归结为两个问题:

  1. 我应该使用方法组而不是lambda吗?是的你应该。这样做没有任何缺点,而且它的代码效率更高,代码更短,而且对可维护性没有任何影响。
  2. 我应该忽略async / await还是保留关键字?这个比较细微。

在这种情况下省略async 很好,因为所有async lambda所做的就是调用一个方法并传递其参数。在调用IsOddAsync之前或之后,lambda不会引发异常。

但是,如果您的lambda更为复杂-在将其传递给x之前对IsOddAsync进行操作,或者对结果进行操作,或者使用using块,那么您可以d希望保留async / await关键字以实现最大的可维护性。更多信息here