一行即发即忘:void 与 async void + await

One line fire and forget: void vs. async void + await

我有一个 void 事件处理程序,它只包含一行调用 awaitable 方法。

void Handler( object sender, EventArgs args ) => AwaitableMethod();

将 return 类型更改为 Task 不是一种选择,因此无论语法糖如何,它都是即发即弃。即便如此,Visual Studio 在未 awaited 调用下方放置了一个绿色波浪线,建议我将处理程序 async voidawait 调用。

async void Handler( object sender, EventArgs args ) => await AwaitableMethod();

我的理解是在这种情况下添加 async voidawait 只是无用的开销。 Visual Studio 是否知道一些我不知道的事情,还是只是不必要地烦人?

在这种特殊情况下,这主要取决于您的错误处理。

如果AwaitableMethod异步抛出,会在第二个版本的UI线程上抛出。

My understanding is that adding async void and await in this case would just be useless overhead.

没有

如果Handler return编辑了一个Task,那就是真的,eliding async/await就可以了;没有 async/await 的代码只会 return 直接 Task 而不是 "unwrapping" 它与 await 和 "wrapping" 它返回变成 Taskasync.

然而,这里不是这种情况; Handler returns void,所以没有 async/await 的代码将忽略 returned Task,这是绝大多数时候都是错误的(因此编译器警告)。具体来说,忽略 Task 将忽略来自 Task 的任何异常。您的代码也无法知道被忽略的 Task 何时完成,但大概这是可以接受的,因为您的处理程序正在 returning void.

async void 方法执行了 "registration" 以便框架知道有一项任务仍在进行中,因此框架知道何时可以安全关闭。 .NET 提供的唯一真正关心的框架是 ASP.NET pre-Core;所有其他 .NET 提供的框架(包括所有 UI 框架)忽略 "registration".

我建议在这种情况下避免使用事件处理程序,并尽可能使用类似这样的东西。

 public class Eventful
    {
        private readonly List<Func<Task>> _handlers = new List<Func<Task>>();

        public void AddHandler(Func<Task> handler) => _handlers.Add(handler);

        private async Task RunHandlers()
        {
            foreach (var handler in _handlers)
            {
                await handler();
            }
        }
    }

    public class Consumer
    {
        public Consumer()
        {
            var eventful = new Eventful();
            eventful.AddHandler(async () =>
            {
                await DoAsyncTask();
            });
        }

        private static async Task DoAsyncTask()
        {
            await Task.CompletedTask;
        }
    }