简体   繁体   English

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

[英]How to avoid nested AggregateException when using Task.ContinueWith?

I have an async method in a .NET 4.5 C# component:我在 .NET 4.5 C# 组件中有一个异步方法:

public async Task<T> GetResultAsync()
{
    return PerformOperationAsync();
}

If PerformOperationAsync throws an exception, then I can catch an AggregateException on a client side, unwrap it and get the original exception thrown.如果 PerformOperationAsync 抛出异常,那么我可以在客户端捕获 AggregateException,解开它并获取抛出的原始异常。

However, if I have slightly more complicated code:但是,如果我有稍微复杂的代码:

public async Task<T> GetResultAsync()
{
    return PerformOperationAsync().ContinueWith(x =>
    {
        var result = x.Result;
        return DoSomethingWithResult(result);
    }, cancellationToken);
}

... then in case an exception occurs, the client catches a nested AggregateException, so it has to flatten it prior getting the original one. ...然后万一发生异常,客户端会捕获一个嵌套的 AggregateException,因此它必须在获取原始异常之前将其展平。

Should this behavior be avoided or does client have to expect possibly nested AggregateException and call Flatten to unwrap all its levels?是否应该避免这种行为,或者客户端是否必须期望可能嵌套的 AggregateException 并调用 Flatten 来展开其所有级别? And if this behavior should be avoided by the component developer, then what's the right way to deal with it in ContinueWith scenario?如果组件开发人员应该避免这种行为,那么在 ContinueWith 场景中处理它的正确方法是什么? I have plenty of similar situations, so I am trying to find the most lightweight method of handling them.我有很多类似的情况,所以我试图找到处理它们的最轻量级的方法。

C#5 async/await will help you deal with continuations and proper exception handling while simplifying the code. C#5 async/await 将帮助您处理延续和适当的异常处理,同时简化代码。

public async Task<T> GetResultAsync()
{
    var result = await PerformOperationAsync().ConfigureAwait(false);
    return DoSomethingWithResult(result);
}

Your method is already marked as async, is it intended ?您的方法已被标记为异步,是有意的吗?


To keep the continuation you can provide a TaskContinuationOptions with OnlyOnRanToCompletion value :为了保持延续,您可以提供一个TaskContinuationOptionsOnlyOnRanToCompletion值:

PerformOperationAsync().ContinueWith(x =>
{
    var result = x.Result;
    return DoSomethingWithResult(result);
}, TaskContinuationOptions.OnlyOnRanToCompletion);

or use the awaiter to raise the original exception或使用 awaiter 引发原始异常

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

In addition to Guillaume's answer, I am using a little extension method除了纪尧姆的回答,我使用了一点扩展方法

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

Instead of var result = x.Result;而不是var result = x.Result; , use it like this: ,像这样使用它:

var result = x.SyncResult();

It does the same as Guillaume explained, but is shorter to use and easier to memorize.它的作用与Guillaume解释的相同,但使用时间更短且更易于记忆。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM