Как лучше понять код / ​​операторы из статьи "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 будет проигнорировано)

В чем проблема:

  1. статья не так?
    Какой код / ​​утверждения и как изменить, чтобы правильно его понять?
  2. Я сделал ошибку при воспроизведении первого фрагмента кода статьи?
  3. Это из-за ошибки в.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
Другие вопросы по тегам