Why is the exception not being caught?

孤人 提交于 2019-12-11 06:33:44

问题


I think there is something missing in my understanding. If I put a breakpoint in the catch where the Console.WriteLine is done, it does not stop.

private static void Main(string[] args)
{
    Process(async () => await ClientMethod()).Invoke();
    Console.Read();
}

public static async Task ClientMethod()
{
    throw new Exception("Test");
}

public static Action Process(Action functor)
{
    return () =>
    {
        try
        {
            functor();
        }
        catch (Exception)
        {
            // Handle exceptions ?
            Console.WriteLine("In the catch");
            throw;
        }
    };
}

But if I change my code into this, by removing the async behavior, the breakpoint is hit:

private static void Main(string[] args)
{
    Process(() => ClientMethod()).Invoke();
    Console.Read();
}

public static void ClientMethod()
{
    throw new Exception("Test");
}

Why the exception is not caught in the first case? How can I catch it?

EDIT: I changed my code into this but it is still the same:

private static void Main(string[] args)
{
    var res = Process(async () => await ClientMethod()).Invoke();
    Console.Read();
}

public static async Task<string> ClientMethod()
{
    throw new Exception("Test");
}

public static Func<T> Process<T>(Func<T> functor)
{
    return () =>
    {
        try
        {
            return functor();
        }
        catch (Exception)
        {
            // Handle exceptions ?
            Console.WriteLine("In the catch");
            throw;
        }
    };
}

回答1:


Because the action in Process is being invoked as an async void.

Quote: from Async/Await - Best Practices in Asynchronous Programming

Async void methods have different error-handling semantics. When an exception is thrown out of an async Task or async Task<T> method, that exception is captured and placed on the Task object. With async void methods, there is no Task object, so any exceptions thrown out of an async void method will be raised directly on the SynchronizationContext that was active when the async void method started.




回答2:


The problem with your first code is that the Action is causing an async void method to be created. You should avoid async void for several reasons - one of which is that exception handling is odd (exceptions do not propagate from an async void method in any kind of reasonable manner).

Your second code is odd. You can see more clearly what's going on if you remember that T is Task<string>. So, your wrapper delegate is just going to return a Task<string>; it won't observe its exceptions. To observe exceptions from a task-returning method, you should await the task it returns, and the code in your try block isn't awaiting the task - it's just returning it directly.

The solution is to go back to the first example. What you want to do is change the Action delegate type to its asynchronous equivalent, which is Func<Task>. From here, the solution flows more naturally:

public static Func<Task> Process(Func<Task> functor)
{
  return async () =>
  {
    try
    {
      await functor();
    }
    catch (Exception)
    {
      Console.WriteLine("In the catch");
      throw;
    }
  };
}

Usage:

await Process(async () => await ClientMethod()).Invoke();

public static async Task ClientMethod()
{
  throw new Exception("Test");
}


来源:https://stackoverflow.com/questions/41979621/why-is-the-exception-not-being-caught

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!