重新抛出内部ContinueWith前一个异常(Rethrowing previous except

2019-07-30 20:19发布

介绍

令人费解了我一会儿代码后,我发现异常不一定通过传播ContinueWith

int zeroOrOne = 1;
Task.Factory.StartNew(() => 3 / zeroOrOne)
    .ContinueWith(t => t.Result * 2)
    .ContinueWith(t => Console.WriteLine(t.Result))
    .ContinueWith(_ => SetBusy(false))
    .LogExceptions();

在这个例子中, SetBusy线“复位”异常链,所以通过零异常的鸿沟是没有看到,随后在我的脸炸毁“任务的异常(S),未观察到......”

所以...我写我自己有点扩展方法(以吨重载不同,但基本上都这样做):

public static Task ContinueWithEx(this Task task, Action<Task> continuation)
{
     return task.ContinueWIth(t =>
     {
         if(t.IsFaulted) throw t.Exception;
         continuation(t);
     });
}

周围多一点搜索,我碰到这个博客帖子,在那里,他提出了一个类似的解决方案,但使用TaskCompletionSource,其中(转述)看起来是这样的:

public static Task ContinueWithEx(this Task task, Action<Task> continuation)
{
     var tcs = new TaskCompletionSource<object>();
     return task.ContinueWith(t =>
     {
         if(t.IsFaulted) tcs.TrySetException(t.Exception);
         continuation(t);
         tcs.TrySetResult(default(object));
     });
     return tcs.Task;
}

在这两个版本完全等同? 或者是有之间细微的差别throw t.Exceptiontcs.TrySetException(t.Exception)

此外,没有一个事实,即有显然只有一个其他人的整个互联网是谁在做这在表明我就是缺少这样做的惯用方法是什么?

Answer 1:

两者之间的区别是很微妙的。 在第一个例子,你扔从任务返回的异常。 这将触发正常的异常抛出,并在CLR捕获,该ContinueWith将捕获并把它包起来,并把它传递给链中的下一个任务。

在第二个你调用TrySetException将仍然包裹异常,并把它传递给链中的下一个任务,但不会触发任何的try / catch逻辑。

一前一后的最终结果ContinueWithExAggregateException(AggregateException(DivideByZeroException)) 我看到的唯一区别是,内AggregateException具有堆栈跟踪在第一示例中设置(因为它被抛出),并在第二个例子中没有堆栈跟踪。

无论是可能比其他显著快,但我个人更喜欢第二个,以避免不必要的罚球。

我做了这样的事情在延续返回的结果。 我把它叫做Select ,处理的前一个任务被取消的情况下,提供过载修改的例外,而不是或除了结果,以及所使用的ExecuteSynchronously选项。 当继续将自身返回一个任务,我称那Then ,而不是基于从代码这篇文章



文章来源: Rethrowing previous exception inside ContinueWith