等待try / catch / finally的一个好的解决scheme?
我需要在catch
块中调用一个async
方法,然后再次抛出exception(带有堆栈跟踪),如下所示:
try { // Do something } catch { // <- Clean things here with async methods throw; }
但不幸的是,你不能用一个catch
或finally
块来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以后,有可能在catch
和finally
块中使用await
,所以你可以这样做:
try { // Do something } catch (Exception ex) { await DoCleanupAsync(); throw; }
新的C#6.0function,包括我刚刚提到的function,在这里列出或作为video在这里 。
如果你需要使用async
error 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)
。