为什么无效async坏?

Ano*_*mer 11 c# async-await

所以我理解为什么从异步中返回void通常没有任何意义,但我遇到了一种我认为完全有效的情况.考虑以下人为的例子:

protected override void OnLoad(EventArgs e)
{
    if (CustomTask == null)
        // Do not await anything, let OnLoad return.
        PrimeCustomTask();
}
private TaskCompletionSource<int> CustomTask;

// I DO NOT care about the return value from this. So why is void bad?
private async void PrimeCustomTask()
{
    CustomTask = new TaskCompletionSource<int>();
    int result = 0;
    try
    {
        // Wait for button click to set the value, but do not block the UI.
        result = await CustomTask.Task;
    }
    catch
    {
        // Handle exceptions
    }
    CustomTask = null;

    // Show the value
    MessageBox.Show(result.ToString());
}

private void button1_Click(object sender, EventArgs e)
{
    if (CustomTask != null)
        CustomTask.SetResult(500);
}
Run Code Online (Sandbox Code Playgroud)

我意识到这是一个不寻常的例子,但我试图让它更简单,更通用.有人可以向我解释为什么这是可怕的代码,以及我如何修改它以正确遵循约定?

谢谢你的帮助.

Ste*_*ary 20

好吧,通过"避免async void"文章中的原因:

  • 异步void方法具有不同的错误处理语义.逃避的例外PrimeCustomTask处理将非常尴尬.
  • 异步void方法具有不同的组合语义.这是一个以代码可维护性和重用为中心的论据.从本质上讲,逻辑就在PrimeCustomTask那里,就是它 - 它不能被组成一个更高层次的async方法.
  • 异步void方法很难测试.从前两点开始,很难编写一个单元测试覆盖PrimeCustomTask(或任何调用它的东西).

同样重要的是要注意这async Task是自然的方法.在采用async/await几种语言中,C#/ VB是唯一支持AFAIK的语言async void.F#没有,Python没有,JavaScript和TypeScript没有.async void从语言设计的角度看是不自然的.

async void添加到C#/ VB 的原因是为了启用异步事件处理程序.如果您更改代码以使用async void事件处理程序:

protected override async void OnLoad(EventArgs e)
{
  if (CustomTask == null)
    await PrimeCustomTask();
}

private async Task PrimeCustomTask()
Run Code Online (Sandbox Code Playgroud)

然后,async void缺点仅限于您的事件处理程序.特别是,异常PrimeCustomTask会自然地传播到它的(异步)调用者(OnLoad),PrimeCustomTask可以组合(从其他异步方法自然地调用),并且PrimeCustomTask更容易包含在单元测试中.


elf*_*elf 8

使用void async通常只视为"坏",因为:

  • 你不能等待它的完成(如本文所述)
  • 任何未处理的异常都将终止您的流程(哎哟!)

有很多案例(比如你的)使用它很好.使用它时要小心.

  • 我真的很喜欢这个答案,因为它放弃了许多“编码指南”通常的绝对主义,而是重申了常识(如果有点太简洁),但也肯定了存在完全有效和正确的用例。 (7认同)