立即从异步方法投掷

async Task方法抛出的exception的正常行为是保持hibernate状态,直到稍后观察它们,或者直到任务被垃圾收集。

我可以想到我可能想立即抛出的情况。 这是一个例子:

 public static async Task TestExAsync(string filename) { // the file is missing, but it may be there again // when the exception gets observed 5 seconds later, // hard to debug if (!System.IO.File.Exists(filename)) throw new System.IO.FileNotFoundException(filename); await Task.Delay(1000); } public static void Main() { var task = TestExAsync("filename"); try { Thread.Sleep(5000); // do other work task.Wait(); // wait and observe } catch (AggregateException ex) { Console.WriteLine(new { ex.InnerException.Message, task.IsCanceled }); } Console.ReadLine(); } 

我可以使用async void绕过它,它会立即抛出:

 // disable the "use await" warning #pragma warning disable 1998 public static async void ThrowNow(Exception ex) { throw ex; } #pragma warning restore 1998 public static async Task TestExAsync(string filename) { if (!System.IO.File.Exists(filename)) ThrowNow(new System.IO.FileNotFoundException(filename)); await Task.Delay(1000); } 

现在我可以使用Dispatcher.UnhandledExceptionAppDomain.CurrentDomain.UnhandledException在现场处理此exception,至少立即引起用户注意。

这种情况还有其他选择吗? 这可能是一个人为的问题吗?

如果你真的想这样做,你可以使用Jon Skeet 在他重新实现LINQ时使用的相同方法:创建一个可以抛出或调用真正的异步方法的同步方法:

 public static Task TestExAsync(string filename) { if (!System.IO.File.Exists(filename)) throw new System.IO.FileNotFoundException(filename); return TestExAsyncImpl(filename); } private static async Task TestExAsyncImpl(string filename) { await Task.Delay(1000); } 

请记住,我认为假设Task返回方法不直接抛出是正常的。 例如,您可以使用Task.WhenAll()在正常情况下从多个操作中获取所有exception,但是当立即抛出exception时,此方法将不起作用。

我认为正常行为是恰当的。 你的线程依赖于 async函数的结果来进行下一次处理,所以应该在你的线程上抛出exception。 然后,您的线程代码可以采取适当的措施从exception中恢复。 因为您可以传递任务并启动许多任务,所以您的恢复代码可能位于需要获取Task结果的其他位置,而不是原始调用代码。 如果立即抛出exception,则可能会将其抛出恢复代码之外

asynch void函数立即抛出,这是有道理的,因为没有任何东西取决于它的结果,并且没有任务传递。

顺便说一下,exception处理的重点是从exception中恢复应用程序状态, 不应该捕获任何无法恢复的exception 。 抛出exception时,您的应用程序状态可能已损坏,尝试继续使用损坏的应用程序会导致更多问题和安全漏洞。