-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
Background and motivation
ConfigureAwaitOptions
were added in .NET 8 and the original motivation was to allow await
to avoid throwing an exception. This is a niche performance feature and I don't see it got much adoption in codebases outside .NET ecosystem itself. Event I myself am not used to this no-throwing pattern enough, so I may get a spark of realization a few days after the original work is done. Hence I believe there can be an analyzer suggesting to replace most common suppress exceptions patterns with this new option. Not only does this improve perf but also reduce nesting since try
is a one nesting level + catch
may add another one
API Proposal
Add an analyzer + code fix to suggest replacing suppress exceptions pattern for single await
operation with ConfigureAwaitOptions.SuppressThrowing
. Concrete example in the section below.
API Usage
- Basic suppress throw:
// Before
try
{
await someExpressionReturinngTask; // or `await someExpressionReturinngTask.ConfigureAwait(true)`
}
catch // or `catch (Exception)` or `catch (Exception ex)`
{
}
// After
await someExpressionReturinngTask.ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext | ConfigureAwaitOptions.SuppressThrowing);
- With
ConfigureAwait(false)
:
// Before
try
{
await someExpressionReturinngTask.ConfigureAwait(false);
}
catch
{
}
// After
await someExpressionReturinngTask.ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing);
- When expression returns
Task<T>
, but the result is ignored:
// Before
try
{
await someExpressionReturinngTaskOfT; // or `_ = await someExpressionReturinngTaskOfT`
}
catch
{
}
// After
await ((Task)someExpressionReturinngTaskOfT).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext | ConfigureAwaitOptions.SuppressThrowing);
- When code inside
catch
does not depend on caught exception object:
// Before
bool hasErrors = false;
try
{
await someExpressionReturinngTask;
}
catch
{
hasErrors = true;
}
// After
var someExpressionTask = someExpressionReturinngTask;
await someExpressionTask.ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext | ConfigureAwaitOptions.SuppressThrowing);
bool hasErrors = !someExpressionTask.IsCompletedSuccessfully;
// Before
try
{
await someExpressionReturinngTask;
}
catch
{
Console.WriteLine("This code does not depend on exception object");
}
// After
var someExpressionTask = someExpressionReturinngTask;
await someExpressionTask.ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext | ConfigureAwaitOptions.SuppressThrowing);
if (!someExpressionTask.IsCompletedSuccessfully)
{
Console.WriteLine("This code does not depend on exception object");
}
And any combinatorial combination of these cases.
Alternative Designs
No response
Risks
No response