2.3 KiB
.NET Async Best Practices: Avoiding async void
This document defines the core rules for handling asynchronous operations in .NET, specifically focused on the dangers of async void.
The Rule: NEVER Use async void
async void is a critical anti-pattern in .NET that must be avoided in almost all scenarios.
Why async void is Dangerous:
- Untraceable Crashes: Exceptions thrown in an
async voidmethod cannot be caught by atry-catchblock outside the method. They often crash the entire process. - Impossible to Await: Callers cannot know when the operation has finished, leading to race conditions and "disposed object" exceptions (especially with
DbContext). - Broken Lifecycle: In Blazor and ASP.NET Core, the DI container might dispose of scoped services (like
DbContext) before theasync voidmethod completes its work.
Correct Patterns
1. Standard Methods
Always return Task or ValueTask instead of void.
// BAD
public async void SaveDataAsync() { ... }
// GOOD
public async Task SaveDataAsync() { ... }
2. Async Events (Blazor/Services)
When dealing with events that are defined as Action or EventHandler, do not use async void in the handler. Instead, refactor the event to support Task.
Pattern A: Use Func<Task> for Events
Instead of Action, use Func<Task> so the invoker can await all handlers.
// Interface
event Func<Task>? OnChanged;
// Invoker
if (OnChanged != null)
{
foreach (var handler in OnChanged.GetInvocationList().Cast<Func<Task>>())
{
await handler();
}
}
Pattern B: Wrapper for Legacy Events
If you must subscribe to a legacy void event, use a Task-returning method and a lambda, but be aware of the "fire-and-forget" risks.
// Better than async void, but still has lifecycle risks
NavigationService.OnChanged += () => { _ = HandleChangedAsync(); };
Special Exceptions
The ONLY valid place for async void is in Top-level Event Handlers in legacy UI frameworks (WinForms/WPF) where the event signature is fixed and cannot be changed. Even then, the method body should be wrapped in a try-catch that logs or handles all exceptions.
In modern Blazor and Web development, there is zero justification for async void.