为什么异常没有被捕获?

时间:2022-05-02 01:55:32

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.

我认为我的理解中缺少一些东西。如果我在完成Console.WriteLine的catch中放置一个断点,它就不会停止。

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;
        }
    };
}

2 个解决方案

#1


3  

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

因为正在调用Process中的操作作为异步void。

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

Quote:来自Async / Await - 异步编程的最佳实践

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.

异步void方法具有不同的错误处理语义。当异步任务或异步Task 方法抛出异常时,将捕获该异常并将其放在Task对象上。使用async void方法,没有Task对象,因此异步void方法抛出的任何异常都将直接在异步void方法启动时处于活动状态的SynchronizationContext上引发。

#2


1  

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).

您的第一个代码的问题是Action正在导致创建异步void方法。您应该避免异步void,原因有几个 - 其中一个原因是异常处理很奇怪(异常不会以任何合理的方式从异步void方法传播)。

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.

你的第二个代码很奇怪。如果你记得T是Task ,你可以更清楚地看到发生了什么。所以,你的包装器委托只是返回一个Task ;它不会遵守它的例外情况。要观察任务返回方法的异常,您应该等待它返回的任务,并且try块中的代码不等待任务 - 它只是直接返回它。

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:

解决方案是回到第一个例子。您要做的是将Action委托类型更改为其异步等效项,即Func 。从这里开始,解决方案更自然地流动:

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");
}

#1


3  

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

因为正在调用Process中的操作作为异步void。

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

Quote:来自Async / Await - 异步编程的最佳实践

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.

异步void方法具有不同的错误处理语义。当异步任务或异步Task 方法抛出异常时,将捕获该异常并将其放在Task对象上。使用async void方法,没有Task对象,因此异步void方法抛出的任何异常都将直接在异步void方法启动时处于活动状态的SynchronizationContext上引发。

#2


1  

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).

您的第一个代码的问题是Action正在导致创建异步void方法。您应该避免异步void,原因有几个 - 其中一个原因是异常处理很奇怪(异常不会以任何合理的方式从异步void方法传播)。

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.

你的第二个代码很奇怪。如果你记得T是Task ,你可以更清楚地看到发生了什么。所以,你的包装器委托只是返回一个Task ;它不会遵守它的例外情况。要观察任务返回方法的异常,您应该等待它返回的任务,并且try块中的代码不等待任务 - 它只是直接返回它。

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:

解决方案是回到第一个例子。您要做的是将Action委托类型更改为其异步等效项,即Func 。从这里开始,解决方案更自然地流动:

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");
}