How it works
A useful operation separates immediate validation from work that actually waits. LoadTotalAsync validates its count before awaiting the prepared loader, then computes a total from the returned values. The caller awaits the whole operation and controls how to display the result. No fire-and-forget task escapes its lifetime, so a failure or cancellation can travel back to the owner. For larger operations, pass a caller token through every cancellable dependency and return a clearly defined result type for expected domain failures.
Keep reusable calculations free of console writes so they can serve both a command-line display and an application interface.
Test a count of zero and then a negative count. Identify the successful empty result and the rejected argument, then add cancellation without changing who owns the operation.
A working example
using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
Console.WriteLine(await LoadTotalAsync(3));
async Task<int> LoadTotalAsync(int count)
{
if (count < 0) throw new ArgumentOutOfRangeException(nameof(count));
var values = await LoadAsync(count);
return values.Sum();
}
async Task<int[]> LoadAsync(int count)
{
await Task.Delay(1);
return Enumerable.Range(1, count).ToArray();
}
Expected output
6
One awaited result should cover the full lifetime of the operation.