C# 在 try/catch/finally 中等待一个好的解决方案?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16626161/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
A good solution for await in try/catch/finally?
提问by user2397050
I need to call an asyncmethod in a catchblock before throwing again the exception (with its stack trace) like this :
在再次抛出异常(及其堆栈跟踪)之前,我需要async在catch块中调用一个方法,如下所示:
try
{
// Do something
}
catch
{
// <- Clean things here with async methods
throw;
}
But unfortunately you can't use awaitin a catchor finallyblock. I learned it's because the compiler doesn't have any way to go back in a catchblock to execute what is after your awaitinstruction or something like that...
但不幸的是,您不能await在 acatch或finally块中使用。我了解到这是因为编译器没有任何方法可以在catch块中返回以执行您的await指令之后的内容或类似的东西......
I tried to use Task.Wait()to replace awaitand I got a deadlock. I searched on the Web how I could avoid this and found this site.
我试图用Task.Wait()替换await,但我陷入了僵局。我在网上搜索如何避免这种情况并找到了这个站点。
Since I can't change the asyncmethods nor do I know if they use ConfigureAwait(false), I created these methods which take a Func<Task>that starts an async method once we are on a different thread (to avoid a deadlock) and waits for its completion:
由于我无法更改这些async方法,也不知道它们是否使用ConfigureAwait(false),因此我创建了这些方法,Func<Task>一旦我们处于不同的线程(以避免死锁)并等待其完成,这些方法就会启动异步方法:
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());
}
So my questions is: Do you think this code is okay?
所以我的问题是:你认为这段代码好吗?
Of course, if you have some enhancements or know a better approach, I'm listening! :)
当然,如果您有一些改进或知道更好的方法,我在听!:)
采纳答案by Stephen Cleary
You can move the logic outside of the catchblock and rethrow the exception after, if needed, by using ExceptionDispatchInfo.
catch如果需要,您可以将逻辑移到块之外并在之后重新抛出异常,使用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();
}
}
This way, when the caller inspects the exception's StackTraceproperty, it still records where inside TaskThatFailsit was thrown.
这样,当调用者检查异常的StackTrace属性时,它仍然会记录TaskThatFails它被抛出的位置。
回答by Stephen Cleary
If you need to use asyncerror handlers, I'd recommend something like this:
如果您需要使用async错误处理程序,我建议您这样做:
Exception exception = null;
try
{
...
}
catch (Exception ex)
{
exception = ex;
}
if (exception != null)
{
...
}
The problem with synchronously blocking on asynccode (regardless of what thread it's running on) is that you're synchronously blocking. In most scenarios, it's better to use await.
同步阻塞async代码(不管它运行在哪个线程上)的问题是你正在同步阻塞。在大多数情况下,最好使用await.
Update:Since you need to rethrow, you can use ExceptionDispatchInfo.
更新:由于您需要重新抛出,您可以使用ExceptionDispatchInfo.
回答by mrts
We extracted hvd's great answerto the following reusable utility class in our project:
我们在我们的项目中提取了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();
}
}
}
}
One would use it as follows:
人们会按如下方式使用它:
public async Task OnDoSomething()
{
await TryWithAwaitInCatch.ExecuteAndHandleErrorAsync(
async () => await DoSomethingAsync(),
async (ex) => { await ShowMessageAsync("Error: " + ex.Message); return false; }
);
}
Feel free to improve the naming, we kept it intentionally verbose. Note that there is no need to capture the context inside the wrapper as it is already captured in the call site, hence ConfigureAwait(false).
随意改进命名,我们故意保持冗长。请注意,无需在包装器内捕获上下文,因为它已在调用站点中捕获,因此ConfigureAwait(false).
回答by Adi Lester
You should know that since C# 6.0, it's possible to use awaitin catchand finallyblocks, so you could in fact do this:
您应该知道,从 C# 6.0 开始,可以使用awaitincatch和finally块,因此您实际上可以这样做:
try
{
// Do something
}
catch (Exception ex)
{
await DoCleanupAsync();
throw;
}
The new C# 6.0 features, including the one I just mentioned are listed hereor as a video here.

