使用 ContinueWith() 时如何获取原始异常?
How to get the original exception when using ContinueWith()?
考虑以下代码:
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");
}
}
}
您需要存储对 Task.Run(() => throwExceptionAfterOneSecond())
的引用,以便稍后检查它的 Exception
属性。这是唯一出错的任务。检查任何其他任务都不会提供该异常。
我也不会依赖 TaskContinuationOptions.NotOnFaulted
,因为这几乎强制使用异常来控制流程。很难等待一个非正常完成的任务而不抛出异常。
.ContinueWith(task => {
if (task.Status == RanToCompletion) Console.WriteLine("ContinueWith()");
}
您可以将 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);
如果您需要捕获 catch
语句中的异常,您可以从 continue 中重新抛出。例如
tasks[0] = Task.Run(() => throwExceptionAfterOneSecond())
.ContinueWith(task =>
{
if (task.IsFaulted)
{
// Throw the inner exception
throw task.Exception.InnerException;
}
Console.WriteLine("ContinueWith()");
});
考虑以下代码:
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");
}
}
}
您需要存储对 Task.Run(() => throwExceptionAfterOneSecond())
的引用,以便稍后检查它的 Exception
属性。这是唯一出错的任务。检查任何其他任务都不会提供该异常。
我也不会依赖 TaskContinuationOptions.NotOnFaulted
,因为这几乎强制使用异常来控制流程。很难等待一个非正常完成的任务而不抛出异常。
.ContinueWith(task => {
if (task.Status == RanToCompletion) Console.WriteLine("ContinueWith()");
}
您可以将 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);
如果您需要捕获 catch
语句中的异常,您可以从 continue 中重新抛出。例如
tasks[0] = Task.Run(() => throwExceptionAfterOneSecond())
.ContinueWith(task =>
{
if (task.IsFaulted)
{
// Throw the inner exception
throw task.Exception.InnerException;
}
Console.WriteLine("ContinueWith()");
});