In certain circumstances, it really makes sense to collect multiple exceptions. This is particularly true when you're waiting for multiple tasks to complete.
// -----------------------------------------------------------------------------
// Task Extension To Collect Multiple Task Exceptions
//
// A cleaner way of reacting to a task’s status, we don’t just try to fetch the
// result and catch any exceptions; we handle each status individually.
//
// MORE SOPHISTICATED (BUT LOSSY) EXCEPTION HANDLING
// http://codeblog.jonskeet.uk/2011/06/22/eduasync-part-11-more-sophisticated-but-lossy-exception-handling/
// -----------------------------------------------------------------------------
public static Task<T> WithAllExceptions<T>(this Task<T> task)
{
TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
task.ContinueWith(ignored =>
{
switch (task.Status)
{
case TaskStatus.Canceled:
tcs.SetCanceled();
break;
case TaskStatus.RanToCompletion:
tcs.SetResult(task.Result);
break;
case TaskStatus.Faulted:
// SetException will automatically wrap the original AggregateException
// in another one. The new wrapper will be removed in TaskAwaiter, leaving
// the original intact.
tcs.SetException(task.Exception);
break;
default:
tcs.SetException(new InvalidOperationException("Continuation called illegally."));
break;
}
});
return tcs.Task;
}
// -------
// Example
// -------
private static async Task<int> AwaitMultipleFailures()
{
try
{
await CauseMultipleFailures().WithAllExceptions();
}
catch (AggregateException e)
{
Console.WriteLine("Caught arbitrary exception: {0}", e);
return e.InnerExceptions.Count;
}
// Nothing went wrong, remarkably!
return 0;
}
private static Task<int> CauseMultipleFailures()
{
// Simplest way of inducing multiple exceptions
Exception[] exceptions = { new IOException(), new ArgumentException() };
TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
tcs.SetException(exceptions);
return tcs.Task;
}