顧名思義,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關鍵字的另一個好處。