Throwing exceptions from ContinueWith

2019-06-20 21:36发布

问题:

I am trying to wrap the exceptions that can be thrown by an async task using ContinueWith(). If I just throw from the continuation action things seem to work, but my debugger claims the exception is unhandled. Am I doing something wrong or is this a Visual Studio problem? Is there a cleaner way to do this, or a way to work around my debugger stopping on what is ultimately a handled exception?

The test below passes and prints "caught wrapped exception as expected", but when I debug it the throw new CustomException line shows as "unhandled by user code".

var task = DoWorkAsync().ContinueWith(t => {
    throw new CustomException("Wrapped", t.Exception.InnerException);  // Debugger reports this unhandled
}, TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously);

try {
    task.Wait();
    Assert.Fail("Expected work to fail");
} catch (AggregateException ag) {
    if (!(ag.InnerException is CustomException))
        throw;
}
Console.WriteLine("Caught wrapped exception as expected");

回答1:

When "Just My Code" is enabled, Visual Studio in some cases will break on the line that throws the exception and display an error message that says "exception not handled by user code." This error is benign. You can press F5 to continue and see the exception-handling behavior that is demonstrated in these examples. To prevent Visual Studio from breaking on the first error, just uncheck the "Just My Code" checkbox under Tools, Options, Debugging, General.

From http://msdn.microsoft.com/en-us/library/dd997415.aspx



回答2:

You don't appear to be "wrapping" the exceptions with a continuation, you seem to be throwing the exception in the continuation. If DoWorkAsync is what can throw an exception, I would "wrap" that in a continuation as follows:

DoWorkAsync().ContinueWith(t=>{
 Console.WriteLine("Error occurred: " + t.Exception);
}, TaskContinuationOptions.OnlyOnFaulted);

Alternatively, if you want to "handle" the exception outside the async method, you could do this:

var task = DoWorkAsync();

task.Wait();
if(task.Exception != null)
{
  Console.WriteLine("Error occurred: " + task.Exception);
}

If you want to transform the thrown exception, you could do something like this:

var task = DoWorkAsync().ContinueWith(t=>{
 if(t.Exception.InnerExceptions[0].GetType() == typeof(TimeoutException))
 {
     throw new BackoffException(t.Exception.InnerExceptions[0]);
 }
}, TaskContinuationOptions.OnlyOnFaulted);

And you could handle that BackoffException like this:

if(task.IsFaulted)
{
   Console.WriteLine(task.Exception.InnerExceptions[0]);
   // TODO: check what type and do something other than WriteLine.
}