Language: C# | Type: CODE_SMELL | Severity: Major
Tags: async-await
Because of the way async/await methods are rewritten by the compiler, any exceptions thrown during the parameters check
are captured in the returned Task and will only be observed when the task is awaited. That could happen far away from the source of the
buggy code or never happen for fire-and-forget tasks.
Therefore it is recommended to split the method into two: an outer method handling the parameter checks (without being
async/await) and an inner method to handle the iterator block with the async/await pattern.
This rule raises an issue when an async method throws any exception derived from ArgumentException and contains the
await keyword.
public static async Task SkipLinesAsync(this TextReader reader, int linesToSkip) // Noncompliant
{
if (reader == null) { throw new ArgumentNullException(nameof(reader)); }
if (linesToSkip < 0) { throw new ArgumentOutOfRangeException(nameof(linesToSkip)); }
for (var i = 0; i < linesToSkip; ++i)
{
var line = await reader.ReadLineAsync().ConfigureAwait(false);
if (line == null) { break; }
}
}
var task = SkipLinesAsync(null, -1); // No exception - captured in the task
await task; // ArgumentNullException thrown here
public static Task SkipLinesAsync(this TextReader reader, int linesToSkip)
{
if (reader == null) { throw new ArgumentNullException(nameof(reader)); }
if (linesToSkip < 0) { throw new ArgumentOutOfRangeException(nameof(linesToSkip)); }
return reader.SkipLinesInternalAsync(linesToSkip);
}
private static async Task SkipLinesInternalAsync(this TextReader reader, int linesToSkip)
{
for (var i = 0; i < linesToSkip; ++i)
{
var line = await reader.ReadLineAsync().ConfigureAwait(false);
if (line == null) { break; }
}
}
var task = SkipLinesAsync(null, -1); // ArgumentNullException thrown here
await task;