【问题标题】:How to convert a void synchronous method to async method?如何将 void 同步方法转换为异步方法?
【发布时间】:2021-04-12 14:29:25
【问题描述】:

所以我有这个只有同步操作的方法:

public void Foo(){
    //do something synchronously
    if(x == 0)
        return;
    else if(x < 5):
        //do something
        return;
    // do something extra
}

由于一些接口更改,该方法必须更改为异步,尽管没有异步操作。为了拥有 async 关键字,它的主体中必须有一个 await 关键字。原始同步方法的等效方法是:

public async Task FooAsync(){
    //do something synchronously
    if(x == 0)
        await Task.CompletedTask;
    else if(x < 5):
        //do something
        await Task.CompletedTask;
    // do something extra
}

我不确定这一点,因为 CompletedTask 属性表明它是成功的操作,但并非所有这些条件都是如此。

编辑:在 cmets 中解决。仅仅因为一个方法用“异步”后缀表示,并不意味着它需要一个async 修饰符。该函数将编写如下所示以遵守界面更改:

public Task FooAsync(){
    //do something synchronously
    if(x == 0)
        return Task.CompletedTask;
    else if(x < 5):
        //do something
        return Task.CompletedTask;
    // do something extra
    return Task.CompletedTask;
}

【问题讨论】:

  • 仅仅因为接口要求它返回一个任务并不意味着它必须被标记为异步。您可以不将其标记为异步,将返回类型设为 Task,然后将其设为 return Task.CompletedTask
  • 只需return Task.CompletedTask 就可以了。忘记asyncawait
  • No....仅仅因为接口声明了带有 -Async 后缀的方法名称并不意味着它必须被标记为异步。在不需要时将其标记为异步只会引入不必要的状态机。
  • @avhhh 接口只会表明它返回一个Task。仅当您必须使用 await 时才需要 async 关键字,并且仅当您的某些代码应在异步调用后运行时才需要 await,而您的代码并非如此。
  • 您可能会觉得这很有趣:Eliding Async and Await。如果发生异常,删除 async/await 会改变方法的行为。

标签: c# async-await


【解决方案1】:

正如其他人所指出的,类似任务的返回类型是异步签名,而async 是异步实现细节Task-returning 方法完全有可能不是async

但是,有一个重要的警告:例外。当异步(即Task-like-returning)方法失败时,预期的语义是捕获异常并将其放置在返回的任务上。

所以,你不想只返回CompletedTask;您还想捕获异常并使用Task.FromException

public Task FooAsync()
{
  try
  {
    //do something synchronously
    if(x == 0)
        return Task.CompletedTask;
    else if(x < 5):
        //do something
        return Task.CompletedTask;
    // do something extra
    return Task.CompletedTask;
  }
  catch (Exception ex)
  {
    return Task.FromException(ex);
  }
}

在这一点上,有相当多的样板文件。您可能更喜欢使用async 没有 await,例如:

#pragma warning disable 1998
public async Task FooAsync()
#pragma warning restore 1998
{
    //do something synchronously
    if(x == 0)
        return;
    else if(x < 5):
        //do something
        return;
    // do something extra
}

这种方法的问题是你会得到编译器警告,除非你包含丑陋的编译指示来禁用它们。如果您发现自己需要在多个地方执行此操作,我建议您使用一些辅助方法 (stolen from my AsyncEx library):

public static class TaskHelper
{
#pragma warning disable 1998
    public static async Task ExecuteAsTask(Action func)
#pragma warning restore 1998
    {
        func();
    }

#pragma warning disable 1998
    public static async Task<T> ExecuteAsTask<T>(Func<T> func)
#pragma warning restore 1998
    {
        return func();
    }
}

这样使用:

public Task FooAsync() => TaskHelper.ExecuteAsTask(() =>
{
    //do something synchronously
    if(x == 0)
        return;
    else if(x < 5):
        //do something
        return;
    // do something extra
});

它允许您快速轻松地包装同步方法。

【讨论】:

  • 再次抱歉 ;),是否建议使用 Task.FromException 包装异常以满足抛出异常的异步等待“合同”?
  • @Basin:它更像是一个类似任务的返回合约,而不是async/await 合约。是的,这是为了提供预期的语义。
猜你喜欢
  • 2019-12-26
  • 1970-01-01
  • 1970-01-01
  • 2010-11-04
  • 1970-01-01
  • 1970-01-01
  • 2019-03-28
  • 1970-01-01
  • 2018-10-11
相关资源
最近更新 更多