使用ContinueWith()时如何获得原始异常?

时间:2016-04-28 10:00:16

标签: c# exception .net-4.0 task

请考虑以下代码:

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Demo
{
    static class Program
    {
        static void Main()
        {
            var tasks = new Task[1];

            tasks[0] = Task.Run(() => throwExceptionAfterOneSecond())
                .ContinueWith(task => {
                    Console.WriteLine("ContinueWith()"); }, TaskContinuationOptions.NotOnFaulted);

            try
            {
                Task.WaitAll(tasks);
            }

            catch (AggregateException ex)
            {
                Console.WriteLine("Exception received: " + ex.InnerExceptions.Single().Message);
            }
        }

        static void throwExceptionAfterOneSecond()
        {
            Thread.Sleep(1000);
            throw new InvalidOperationException("TEST");
        }
    }
}

这会产生以下输出:

Exception received: A task was canceled.

我的问题很简单:如何获取原始InvalidOperationException("TEST");而不是System.Threading.Tasks.TaskCanceledException

请注意,如果您删除.ContinueWith()部分,则此操作符合我的预期,在这种情况下的输出为Exception received: TEST

(另请注意,此示例使用.Net 4.5,但原始代码必须使用.Net 4.0)

感谢答案,现在正在运作。我选择了以下解决方案 - 我需要等待原始任务和继续任务:

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Demo
{
    static class Program
    {
        static void Main()
        {
            var tasks = new Task[2];

            tasks[0] = Task.Run(() => throwExceptionAfterOneSecond());

            tasks[1] = tasks[0].ContinueWith(task => {
                if (task.Status == TaskStatus.RanToCompletion)
                    Console.WriteLine("ContinueWith()"); });
            try
            {
                Task.WaitAll(tasks);
            }

            catch (AggregateException ex)
            {
                Console.WriteLine("Exception received: " + ex.InnerExceptions.Single().Message);
            }

            Console.WriteLine("Done.");
        }

        static void throwExceptionAfterOneSecond()
        {
            Thread.Sleep(1000);
            throw new InvalidOperationException("TEST");
        }
    }
}

3 个答案:

答案 0 :(得分:3)

如果您需要在catch语句中捕获异常,可以从continue内部重新抛出。 E.g。

tasks[0] = Task.Run(() => throwExceptionAfterOneSecond())
    .ContinueWith(task =>
    {
        if (task.IsFaulted)
        {
            // Throw the inner exception
            throw task.Exception.InnerException;
        }

        Console.WriteLine("ContinueWith()");
    });

答案 1 :(得分:2)

您需要存储对Exception的引用,以便稍后检查它的TaskContinuationOptions.NotOnFaulted属性。这是唯一出错的任务。检查任何其他任务都不会提供该例外。

我也不依赖.ContinueWith(task => { if (task.Status == RanToCompletion) Console.WriteLine("ContinueWith()"); } 因为这很大程度上强制使用控制流的异常。在没有抛出异常的情况下很难等待非正常完成的任务。

public class Planet {
   public double xxPos; //its current x position
   public double yyPos; //its current y position
   public double xxVel; //its current veolicity in the x direction
   public double yyVel; //its current veolicity in the y direction
   public double mass; //its mass
   public String imgFileName; //The name of an image in the images directory that depicts the planet

   // constructor is like __init__ from python, this sets up the object when called like: Planet(arguments)
   public Planet(double xP, double yP, double xV, double yV, double m, String img) {
      xxPos = xP;
      yyPos = yP;
      xxVel = xV;
      yyVel = yV;
      mass = m;
      imgFileName = img;

   }

   // second constructor
   // how come testplanetconstructor knows to use this second one?
   // does it know based on the argument type its being passed?
   public Planet(Planet p) {
      xxPos = p.xxPos;
      yyPos = p.yyPos;
      xxVel = p.xxVel;
      yyVel = p.yyVel;
      mass = p.mass;
      imgFileName = p.imgFileName;
   }
}

答案 2 :(得分:1)

你可以将ContinueWith部分分开,以便在例外的情况下它们是分开的,并且在成功的情况下是分开的。这是一个例子:

var tasks = new Task[1];
tasks[0] = Task.Run(() => throwExceptionAfterOneSecond());

// For error handling.
tasks[0].ContinueWith(task =>
    {
        // Your logic to handle the exception goes here

        // Aggregate exception
        Console.WriteLine(task.Exception.Message);

        // Inner exception, which is your custom exception
        Console.WriteLine(task.Exception.InnerException.Message);
    },
    TaskContinuationOptions.OnlyOnFaulted);

// If it succeeded.
tasks[0].ContinueWith(task => 
{
    // success
    Console.WriteLine("ContinueWith()");
},TaskContinuationOptions.OnlyOnRanToCompletion);