C# – Catching AggregateException

ctask-parallel-library

I am trying to throw and catch an AggregateException.
I did not use exceptions very much on C#, but the behaviour I found is a little bit surprising.

My code is:

var numbers = Enumerable.Range(0, 20);

try
{
    var parallelResult = numbers.AsParallel()
        .Where(i => IsEven(i));
    parallelResult.ForAll(e => Console.WriteLine(e));

}
catch (AggregateException e)
{
    Console.WriteLine("There was {0} exceptions", e.InnerExceptions.Count());
}

It is calling the function IsEven

private static bool IsEven(int i)
{
    if (i % 10 == 0)
        throw new AggregateException("i");
    return i % 2 == 0;
}

That throws the AggregateException.

I would expect the code to write every even number in the 0,20 range and "There was 1 exceptions" twice.

What I get is some numbers printed (they are random cause of ForAll) and then the exception is thrown, but not catched and the programs stop.

Am i missing something?

Best Answer

This is actually kind of interesting. I think the problem is that you're using AggregateException in an unexpected way, which is causing an error inside the PLINQ code.

The entire point of AggregateException is to group together multiple exceptions that may occur simultaneously (or nearly so) in a parallel process. So AggregateException is expected to have at least one inner exception. But you're throwing new AggregateException("i"), which has no inner exceptions. The PLINQ code tries to examine the InnerExceptions property, hits some sort of error (probably a NullPointerException) and then it seems to go into a loop of some sort. This is arguably a bug in PLINQ, since you're using a valid constructor for AggregateException, even if it is an unusual one.

As pointed out elsewhere, throwing ArgumentException would be more semantically correct. But you can get the behavior you're looking for by throwing a correctly-constructed AggregateException, for example by changing the IsEven function to something like this:

private static bool IsEven(int i)
{
    if (i % 10 == 0){
        //This is still weird
        //You shouldn't do this. Just throw the ArgumentException.
        throw new AggregateException(new ArgumentException("I hate multiples of 10"));
    }
    return i % 2 == 0;
}

I think the moral of the story is to not throw AggregateException unless you really know exactly what you're doing, particularly if you're already inside a parallel or Task-based operation of some kind.

Related Topic