C# allows you to define async delegates or lambdas and use them in contexts that accept
void-returning delegates, thus creating an async void
method such as is forbidden by
VSTHRD100, but is much harder to catch when simply looking at the code
because for the same syntax, the C# compiler will create an async Func<Task>
delegate
or an async void
delegate based on the type expected by the method being invoked.
This analyzer helps prevent inadvertent creation of async void
delegates.
void StartWatching(ObservableCollection<string> oc)
{
// This delegate becomes an "async void" method to match the EventHandler delegate type.
oc.CollectionChanged += async () =>
{
await Task.Yield();
};
}
void StartWatching(ObservableCollection<string> oc)
{
// This delegate becomes an "async void" method to match the Action delegate type.
Callback(async () =>
{
await Task.Yield();
});
}
void Callback(Action action)
{
// out of scope of sample
}
- Wrap the asynchronous behavior in another method that accepts a
Func<Task>
delegate. - Change the receiving method's expected delegate type to one that returns a
Task
orTask<T>
. - Implement the delegate synchronously.
void StartWatching(ObservableCollection<string> oc)
{
oc.CollectionChanged += () =>
{
// The outer delegate is synchronous, but kicks off async work via a method that accepts an async delegate.
joinableTaskFactory.RunAsync(async delegate {
await Task.Yield();
});
};
}
void StartWatching(ObservableCollection<string> oc)
{
// This delegate becomes an "async Task" method to match the Func<Task> delegate type.
Callback(async () =>
{
await Task.Yield();
});
}
void Callback(Func<Task> action)
{
// out of scope of sample
}
Refer to Async/Await - Best Practices in Asynchronous Programming for more info.