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:
I agree with others: this is a bug in .Net and you should report it.
The cause is in the method QueryEnd() in the internal class QueryTaskGroupState.
Its decompiled (and slightly modified for clarity) code looks like this:
try
{
this.m_rootTask.Wait();
}
catch (AggregateException ex)
{
AggregateException aggregateException = ex.Flatten();
bool cacellation = true;
for (int i = 0; i < aggregateException.InnerExceptions.Count; ++i)
{
var canceledException =
aggregateException.InnerExceptions[i] as OperationCanceledException;
if (IsCancellation(canceledException))
{
cacellation = false;
break;
}
}
if (!cacellation)
throw aggregateException;
}
finally
{
this.m_rootTask.Dispose();
}
if (!this.m_cancellationState.MergedCancellationToken.IsCancellationRequested)
return;
if (!this.m_cancellationState.TopLevelDisposedFlag.Value)
CancellationState.ThrowWithStandardMessageIfCanceled(
this.m_cancellationState.ExternalCancellationToken);
if (!userInitiatedDispose)
throw new ObjectDisposedException(
"enumerator", "The query enumerator has been disposed.");
Basically, what this does is:
AggregateException if it contains any non-cancellation exceptionsObjectDisposedException for some reason (assuming userInitiatedDispose is false, which it is)So, if you throw an AggregateException with no inner exceptions, ex will be an AggregateException containing your empty AggregateExcaption. Calling Flatten() will turn that into just an empty AggreateException, which means it doesn't contain any non-cancellation exception, so the first part of the code thinks this is cancellation and doesn't throw.
But the second part of the code realizes this isn't cancellation, so it throws a completely bogus exception.