使用Task.ContinueWith时如何避免嵌套的AggregateException?

时间:2015-04-14 07:00:43

标签: c# exception asynchronous task-parallel-library

我在.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并调用Flatten来解包其所有级别?如果组件开发人员应该避免这种行为,那么在ContinueWith场景中处理它的正确方法是什么?我有很多类似的情况,所以我试图找到最轻量级的处理方法。

2 个答案:

答案 0 :(得分:4)

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);

或使用awaiter引发原始异常

PerformOperationAsync().ContinueWith(x =>
{
    var result = x.GetAwaiter().GetResult();
    return DoSomethingWithResult(result);
}, cancellationToken);

答案 1 :(得分:0)

除了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相同,但使用时间较短且易于记忆。