如何更好地理解“异步 – 处理多个exception”一文中的代码/语句?

运行以下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 – Handling multiple Exceptions”博客文章中再现第一个片段,该文章讲述了:

以下代码将捕获单个NullReferenceException或ArgumentExceptionexception(将忽略AggregateException)

问题出在哪儿:

  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仅包含抛出的一个exception( NullReferenceExceptionArgumentException具体取决于子Tasks的执行顺序)

最有可能的是,这篇文章仍然是正确的,或者至少非常有用。 我只需要了解如何更好地阅读/理解/使用它

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

这篇文章错了。 运行代码时, await ed Task包含一个如下所示的exception:

 AggregateException AggregateException NullReferenceException AggregateException ArgumentException 

await (或者更确切地说, TaskAwaiter.GetResult() )在这里做的是它接受外部AggregateException并重新抛出其第一个子exception。 在这里,这是另一个AggregateException ,所以抛出的是什么。

代码示例,其中一个Task有多个exception,其中一个在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文档中(强调我的):

如果任何提供的任务在故障状态下完成,则返回的任务也将在故障状态下完成,其中exception将包含来自每个提供的任务的一组未包装的exception的聚合。

因此Task的exception将如下所示:

 AggregateException NullReferenceException ArgumentException