等待try / catch / finally的一个好的解决scheme?

我需要在catch块中调用一个async方法,然后再次抛出exception(带有堆栈跟踪),如下所示:

 try { // Do something } catch { // <- Clean things here with async methods throw; } 

但不幸的是,你不能用一个catchfinally块来await 。 我知道这是因为编译器没有办法回到catch块去执行你的await指令之后的东西或类似的东西…

我试图使用Task.Wait()来取代await ,我有一个僵局。 我在网上search如何避免这个,并find了这个网站 。

由于我不能更改async方法,也不知道他们是否使用ConfigureAwait(false) ,所以我创build了这些方法,这些方法采用一个Func<Task> ,一旦我们在另一个线程上就启动一个asynchronous方法(以避免死锁)并等待完成:

 public static void AwaitTaskSync(Func<Task> action) { Task.Run(async () => await action().ConfigureAwait(false)).Wait(); } public static TResult AwaitTaskSync<TResult>(Func<Task<TResult>> action) { return Task.Run(async () => await action().ConfigureAwait(false)).Result; } public static void AwaitSync(Func<IAsyncAction> action) { AwaitTaskSync(() => action().AsTask()); } public static TResult AwaitSync<TResult>(Func<IAsyncOperation<TResult>> action) { return AwaitTaskSync(() => action().AsTask()); } 

所以我的问题是:你认为这个代码好吗?

当然,如果你有一些改进或更好的方法,我正在听! 🙂

您可以移动catch块之外的逻辑,并在需要时使用ExceptionDispatchInfo重新抛出ExceptionDispatchInfo

 static async Task f() { ExceptionDispatchInfo capturedException = null; try { await TaskThatFails(); } catch (MyException ex) { capturedException = ExceptionDispatchInfo.Capture(ex); } if (capturedException != null) { await ExceptionHandler(); capturedException.Throw(); } } 

这样,当调用者检查exception的StackTrace属性时,它仍然会logging它抛出的TaskThatFails内部的TaskThatFails

你应该知道,自从C#6.0以后,有可能在catchfinally块中使用await ,所以你可以这样做:

 try { // Do something } catch (Exception ex) { await DoCleanupAsync(); throw; } 

新的C#6.0function,包括我刚刚提到的function,在这里列出或作为video在这里 。

如果你需要使用asyncerror handling程序,我会推荐这样的:

 Exception exception = null; try { ... } catch (Exception ex) { exception = ex; } if (exception != null) { ... } 

async代码上同步阻塞的问题(无论它在哪个线程上运行)是你正在同步阻塞。 在大多数情况下,最好使用await

更新:因为你需要重新抛出,你可以使用ExceptionDispatchInfo

我们在我们的项目中提取了hvd的以下可重用工具类的很好的答案 :

 public static class TryWithAwaitInCatch { public static async Task ExecuteAndHandleErrorAsync(Func<Task> actionAsync, Func<Exception, Task<bool>> errorHandlerAsync) { ExceptionDispatchInfo capturedException = null; try { await actionAsync().ConfigureAwait(false); } catch (Exception ex) { capturedException = ExceptionDispatchInfo.Capture(ex); } if (capturedException != null) { bool needsThrow = await errorHandlerAsync(capturedException.SourceException).ConfigureAwait(false); if (needsThrow) { capturedException.Throw(); } } } } 

人们会使用它如下:

  public async Task OnDoSomething() { await TryWithAwaitInCatch.ExecuteAndHandleErrorAsync( async () => await DoSomethingAsync(), async (ex) => { await ShowMessageAsync("Error: " + ex.Message); return false; } ); } 

随意改进命名,我们保持它故意冗长。 请注意,不需要捕获包装器中的上下文,因为它已经在调用站点中捕获,因此ConfigureAwait(false)