It's easy enough to throw a single exception, but what if you want to detect if multiple exceptions would be thrown at once?
You can't immediately throw. When using throw, the operation you are currently running will stop, and the application will find itself either in a catch block, or halted if the exception was unhandled.
The trick then, is to collect all the exceptions that have occurred, and throw them all at once as an aggregate exception.
What is an aggregate exception?
An aggregate is a single element formed by adding together multiple separate elements.
An aggregate exception therefore, is an single exception that is composed of multiple separate exceptions.
How do I create an aggregate exception?
You can create an aggregate exception by passing in a message, followed by a collection of Exception objects:
try
{
var exceptions = new List<Exception>();
exceptions.Add(new ArgumentException("Argument Exception Message"));
exceptions.Add(new NullReferenceException("Null Reference Exception Message"));
throw new AggregateException("Aggregate Exception Message", exceptions);
}
catch (AggregateException e)
{
Console.WriteLine(e.Message);
}
In this example, writing the message of the aggregate exception would display the following:
Aggregate Exception Message (Argument Exception Message) (Null Reference Exception Message)
You can then access each exception from within the aggregate:
catch (AggregateException ex)
{
foreach (Exception innerException in ex.InnerExceptions)
{
Console.WriteLine(innerException.Message);
}
}
Where would I use this?
Let's take a look at a simple example, where we input a name into a function:
static void Main ()
{
try
{
PrintName("Sean");
}
catch (Exception ex)
{
Console.WriteLine("Name not valid");
}
}
static void PrintName(string name)
{
// Some logic and exception handling goes here
}
If the name isn't valid, I want to throw an exception specifying the reason.
But what if there are multiple reasons that a name isn't valid? For instance, I want to throw an exception if:
- The name is smaller than 3 characters long
- The name contains any numerical characters
We could throw an exception for each case. However, if the exception is passed to the UI as a validation message, a user would likely get frustrated that they can't see all of the reasons at once.
Let's see the two exception cases implemented:
using System;
using System.Collections.Generic;
using System.Linq;
class AggregateExceptions
{
static void Main ()
{
try
{
PrintName("S1");
}
catch (AggregateException ex)
{
Console.WriteLine(ex.Message);
}
}
static void PrintName(string name)
{
// Collect the Exception objects, rather than immediately throw them
var exceptions = new List<Exception>();
if (name.Length < 3)
{
exceptions.Add(new ArgumentException("The name is not long enough!"));
}
if (name.Any(char.IsDigit))
{
exceptions.Add(new ArgumentException("The name has numbers in it!"));
}
// If we have any exceptions...
if (exceptions.Any())
{
// ...throw them all at once!
throw new AggregateException("Name not valid", exceptions);
}
Console.WriteLine($"Hello {name}!");
}
}
Since we broke both rules at the same time, we get the following output:
Name not valid (The name is not long enough!) (The name has numbers in it!)