Как лучше понять код / операторы из статьи "Async - Обработка нескольких исключений"?
Запуск следующего консольного приложения C#
class Program
{ static void Main(string[] args)
{ Tst();
Console.ReadLine();
}
async static Task Tst()
{
try
{
await Task.Factory.StartNew
(() =>
{
Task.Factory.StartNew
(() =>
{ throw new NullReferenceException(); }
, TaskCreationOptions.AttachedToParent
);
Task.Factory.StartNew
( () =>
{ throw new ArgumentException(); }
,TaskCreationOptions.AttachedToParent
);
}
);
}
catch (AggregateException ex)
{
// this catch will never be target
Console.WriteLine("** {0} **", ex.GetType().Name);
//****** Update1 - Start of Added code
foreach (var exc in ex.Flatten().InnerExceptions)
{
Console.WriteLine(exc.GetType().Name);
}
//****** Update1 - End of Added code
}
catch (Exception ex)
{
Console.WriteLine("## {0} ##", ex.GetType().Name);
}
}
производит вывод:
** AggregateException **
Тем не менее, приведенный выше код воспроизводит первый фрагмент из статьи блога "Async - обработка нескольких исключений", в которой рассказывается об этом:
следующий код перехватит одно исключение NullReference Exception или ArgumentException (Aggregate Exception будет проигнорировано)
В чем проблема:
- статья не так?
Какой код / утверждения и как изменить, чтобы правильно его понять? - Я сделал ошибку при воспроизведении первого фрагмента кода статьи?
- Это из-за ошибки в.NET 4.0/VS2010 расширение Async CTP, я использую?
Update1 (в ответ на ответ svick)
После добавления кода
//****** Update1 - Start of Added code
foreach (var exc in ex.Flatten().InnerExceptions)
{
Console.WriteLine(exc.GetType().Name);
}
//****** Update1 - End of Added code
произведенная продукция:
** AggregateException **
NullReferenceException
Итак, как также прокомментировал Мэтт Смит:
AggregateException
который пойман, содержит только одно из исключений, которые были выброшены (либоNullReferenceException
илиArgumentException
в зависимости от порядка выполнения дочерних заданий)
Скорее всего, статья все еще верна или, по крайней мере, очень полезна. Мне просто нужно понять, как лучше читать / понимать / использовать это
Update2 (в ответ на ответ svick)
Выполнение кода svick:
internal class Program
{
private static void Main(string[] args)
{
Tst();
Console.ReadLine();
}
private static async Task Tst()
{
try
{
await TaskEx.WhenAll
(
Task.Factory.StartNew
(() =>
{ throw new NullReferenceException(); }
//, TaskCreationOptions.AttachedToParent
),
Task.Factory.StartNew
(() =>
{ throw new ArgumentException(); }
//,TaskCreationOptions.AttachedToParent
)
);
}
catch (AggregateException ex)
{
// this catch will never be target
Console.WriteLine("** {0} **", ex.GetType().Name);
//****** Update1 - Start of Added code
foreach (var exc in ex.Flatten().InnerExceptions)
{
Console.WriteLine("==="+exc.GetType().Name);
}
//****** Update1 - End of Added code
}
catch (Exception ex)
{
Console.WriteLine("## {0} ##", ex.GetType().Name);
}
}
}
производит:
## NullReferenceException ##
выход.
Почему нет AggregateException
производится или пойман?
2 ответа
Статья неверна. Когда вы запускаете свой код, await
издание Task
содержит исключение, которое выглядит примерно так:
AggregateException
AggregateException
NullReferenceException
AggregateException
ArgumentException
Какие await
(или, более конкретно, TaskAwaiter.GetResult()
а) вот что берет внешнее AggregateException
и отбрасывает свое первое дочернее исключение. Вот это другое AggregateException
вот так вот бросают.
Пример кода, где Task
имеет несколько исключений, и одно из них сразу перебрасывается после await
будет использовать Task.WhenAll()
вместо AttachedToParent
:
try
{
await Task.WhenAll(
Task.Factory.StartNew(() => { throw new NullReferenceException(); }),
Task.Factory.StartNew(() => { throw new ArgumentException(); }));
}
catch (AggregateException ex)
{
// this catch will never be target
Console.WriteLine("** {0} **", ex.GetType().Name);
}
catch (Exception ex)
{
Console.WriteLine("## {0} ##", ex.GetType().Name);
}
В ответ на ваше "Обновление 2" причина остается той же, что и в ответе svick. Задача содержит AggregateException
, но в ожидании первого броска InnerException
,
Дополнительная информация, которая вам нужна, находится в документации Task.WhenAll (выделено мое):
Если любая из предоставленных задач завершается в состоянии Failed, возвращаемая задача также завершается в состоянии Failed, где ее исключения будут содержать агрегацию набора развернутых исключений для каждой из предоставленных задач.
Так что исключения Task будут выглядеть так:
AggregateException
NullReferenceException
ArgumentException