使用 Task.ContinueWith 时如何避免嵌套的 AggregateException?
How to avoid nested AggregateException when using Task.ContinueWith?
我在 .NET 4.5 C# 组件中有一个异步方法:
public async Task<T> GetResultAsync()
{
return PerformOperationAsync();
}
如果 PerformOperationAsync 抛出异常,那么我可以在客户端捕获 AggregateException,将其解包并抛出原始异常。
但是,如果我有稍微复杂一点的代码:
public async Task<T> GetResultAsync()
{
return PerformOperationAsync().ContinueWith(x =>
{
var result = x.Result;
return DoSomethingWithResult(result);
}, cancellationToken);
}
...然后万一发生异常,客户端会捕获一个嵌套的 AggregateException,因此它必须在获取原始 AggregateException 之前将其展平。
是否应该避免这种行为,或者客户端是否必须期待可能嵌套的 AggregateException 并调用 Flatten 来展开所有级别?如果组件开发人员应该避免这种行为,那么在 ContinueWith 场景中处理它的正确方法是什么?我有很多类似的情况,所以我试图找到最轻量级的处理方法。
C#5 async/await 将帮助您处理延续和正确的异常处理,同时简化代码。
public async Task<T> GetResultAsync()
{
var result = await PerformOperationAsync().ConfigureAwait(false);
return DoSomethingWithResult(result);
}
您的方法已被标记为异步,是故意的吗?
要保持延续,您可以提供 TaskContinuationOptions
和 OnlyOnRanToCompletion
值:
PerformOperationAsync().ContinueWith(x =>
{
var result = x.Result;
return DoSomethingWithResult(result);
}, TaskContinuationOptions.OnlyOnRanToCompletion);
或使用等待程序引发原始异常
PerformOperationAsync().ContinueWith(x =>
{
var result = x.GetAwaiter().GetResult();
return DoSomethingWithResult(result);
}, cancellationToken);
除了Guillaume's回答之外,我正在使用一点扩展方法
public static class Ext
{
/// <summary>
/// Wait for task synchronously, then return result. Avoid AggregateExceptions
/// as it would be generated by asyncTask.Result.
/// </summary>
public static T SyncResult<T>(this Task<T> asyncTask)
=> asyncTask.GetAwaiter().GetResult();
}
而不是var result = x.Result;
,像这样使用它:
var result = x.SyncResult();
它与 Guillaume 解释的相同,但使用起来更短且更容易记忆。
我在 .NET 4.5 C# 组件中有一个异步方法:
public async Task<T> GetResultAsync()
{
return PerformOperationAsync();
}
如果 PerformOperationAsync 抛出异常,那么我可以在客户端捕获 AggregateException,将其解包并抛出原始异常。
但是,如果我有稍微复杂一点的代码:
public async Task<T> GetResultAsync()
{
return PerformOperationAsync().ContinueWith(x =>
{
var result = x.Result;
return DoSomethingWithResult(result);
}, cancellationToken);
}
...然后万一发生异常,客户端会捕获一个嵌套的 AggregateException,因此它必须在获取原始 AggregateException 之前将其展平。
是否应该避免这种行为,或者客户端是否必须期待可能嵌套的 AggregateException 并调用 Flatten 来展开所有级别?如果组件开发人员应该避免这种行为,那么在 ContinueWith 场景中处理它的正确方法是什么?我有很多类似的情况,所以我试图找到最轻量级的处理方法。
C#5 async/await 将帮助您处理延续和正确的异常处理,同时简化代码。
public async Task<T> GetResultAsync()
{
var result = await PerformOperationAsync().ConfigureAwait(false);
return DoSomethingWithResult(result);
}
您的方法已被标记为异步,是故意的吗?
要保持延续,您可以提供 TaskContinuationOptions
和 OnlyOnRanToCompletion
值:
PerformOperationAsync().ContinueWith(x =>
{
var result = x.Result;
return DoSomethingWithResult(result);
}, TaskContinuationOptions.OnlyOnRanToCompletion);
或使用等待程序引发原始异常
PerformOperationAsync().ContinueWith(x =>
{
var result = x.GetAwaiter().GetResult();
return DoSomethingWithResult(result);
}, cancellationToken);
除了Guillaume's回答之外,我正在使用一点扩展方法
public static class Ext
{
/// <summary>
/// Wait for task synchronously, then return result. Avoid AggregateExceptions
/// as it would be generated by asyncTask.Result.
/// </summary>
public static T SyncResult<T>(this Task<T> asyncTask)
=> asyncTask.GetAwaiter().GetResult();
}
而不是var result = x.Result;
,像这样使用它:
var result = x.SyncResult();
它与 Guillaume 解释的相同,但使用起来更短且更容易记忆。