顾名思义,AggregateException用于在单个异常中对一个或多个异常进行批处理。在本文中,我将向您展示为什么会发生此异常,以及如何在C代码中调试它。
错误的产生和处理
让我们从强制一个产生新的AggregateException开始。这个异常在.NET的任务库中被大量使用,为什么选择一个包含任务的示例是一件轻而易举的事情:
Task task1 = Task.Factory.StartNew(() => { throw new ArgumentException(); } ); Task task2 = Task.Factory.StartNew(() => { throw new UnauthorizedAccessException(); } ); try { Task.WaitAll(task1, task2); } catch (AggregateException ae) { }
在上面的例子中,我们执行两个任务,每个任务都抛出一个异常。通过调用WaitAll,我们告诉.NET调用并等待这两个任务的结果。这两个任务的组合结果将是AggregateException。
调试这个错误
从调试器中的上一个示例检查异常时,我们在InnerExceptions属性中看到两个抛出的异常:
try { ... } catch (ArgumentException ex1) { // Log and re-throw } catch (UnauthorizedAccessException ex2) { // Log and swallow }
这样,您就可以向用户生成个别的错误消息,记录异常,但只能重新抛出其中一条或第三条。AggregateException提供了一个方便的名为Handle的小助手方法,而不是在InnerExceptions属性中的每个异常上循环:
try { ... } catch (AggregateException ae) { ae.Handle(inner => { // Log inner ... return inner is UnauthorizedAccessException; }); }
在本例中,Handle方法记录每个异常。func需要返回一个bool,指示是否处理了每个异常。在本例中,我们告诉Handle方法处理了UnauthorizedAccessException,但没有处理ArgumentException。这将导致AggregateException被抛出回调用代码,但InnerExceptions属性中没有UnauthorizedAccessException(因为我们将该异常标记为已处理)。
来自HttpClient的AggregateExceptions
try { var client = new HttpClient(); var task = client.GetStringAsync("https://httpstat.us/500"); task.Wait(); var result = task.Result; } catch (AggregateException ex) { throw ex; }
对https://httpstat.us/500的请求返回一个HTTP状态代码500,该代码抛出一个HttpRequestException。我使用GetStringAsync方法作为示例,但是如果使用其他异步消息(如PostAsync),代码看起来会很相似。如前所述,taskapi的异常被包装在AggregateException中,在InnerExceptions属性中包含HTTP异常。
要获得实际的异常,您有一系列选项。我将重复使用记录异常的必要性来说明。让我们从我在上一个示例中向您展示的Handle方法开始:
try { ... } catch (AggregateException ex) { ex.Handle(inner => { if (inner is HttpRequestException) { // Log the exception return true; } return false; }); }
我正在检查内部异常是否是HttpRequestException类型,在这种情况下,告诉Handle方法这个异常已经被处理。在任何其他场景中,我告诉Handle重新抛出原始异常(通过返回false)。
另一种方法是使用ContinueWith方法捕获和处理AggregateException:
var client = new HttpClient(); var task = client .GetStringAsync("https://httpstat.us/500") .ContinueWith(t => { try { return t.Result; } catch (AggregateException ex) { ex.Handle(inner => { if (inner is HttpRequestException) { // Log the exception return true; } return false; }); } catch (Exception ex) { throw ex; } return null; }); task.Wait(); var result = task.Result;
最后,如果您在.NET4.5上(可能是,如果不是,应该是),则可以使用await关键字:
var client = new HttpClient(); try { var result = await client.GetStringAsync("https://httpstat.us/500"); } catch (HttpRequestException ex) { // Log the exception } catch (Exception ex) { throw ex; }
注意代码是如何捕获HttpRequestException而不是AggregateException的。这是因为.NET会自动打开AggregateException并引发底层异常。在大多数情况下,这就是你想要的。移植现有异步代码以使用await关键字的另一个好处。