微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

c# – 在ContinueWith中重新发现之前的异常

介绍

在我的代码困惑了一段时间后,我发现异常并不一定通过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行’重置’异常链,所以除了零异常不会被看到,随后在我的脸上吹起了“一个任务的异常没有被观察到…”

所以…我写了一个自己的一些扩展方法(吨不同的重载,但基本上都这样做):

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

搜索一下,我发现了this博客文章,他在那里提出了一个类似的解决方案,但是使用了一个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;
}

这两个版本是否严格等同?或者在t.Exception和tcs.TrySetException(t.Exception)之间有微妙的区别?

另外,事实上,整个互联网上显然只有一个人是这样做的,这表明我错过了这样做的惯用方式?

解决方法

两者之间的区别是微妙的.在第一个示例中,您将抛出该任务返回的异常.这将触发正常的异常在CLR中抛出和捕获,ContinueWith将捕获并包装它并将其传递给链中的下一个任务.

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

一个ContinueWithEx之后的最终结果是AggregateException(AggregateException(DivideByZeroException))).我唯一的区别是,内部的AggregateException在第一个例子中设置了一个堆栈跟踪(因为它被抛出),而在第二个例子中没有堆栈跟踪.

也不可能比另一个快得多,但我个人更喜欢第二个来避免不必要的投掷.

我做了这样的事情,继续返回结果.我调用它选择,处理前一个任务被取消的情况,提供重载来修改异常,而不是或除了结果,并使用ExecuteSynchronously选项.当继续本身返回一个Task时,我调用Then而不是基于this article代码

原文地址:https://www.jb51.cc/csharp/96704.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐