使用异步委托进行异常处理

本文关键字:异常处理 异步 | 更新日期: 2023-09-27 18:32:21

我试图弄清楚我在这里做错了什么。我确定这是愚蠢的东西,但我可以用一些额外的眼睛来弄清楚它是什么。我正在尝试创建一个可以向其分配工作操作委托的工作器服务。当我尝试捕获工作操作委托引发的异常时,不会调用处理程序。我认为这是因为我的操作实际上是一个返回 Task 的异步方法,并且没有等待它。但是我怎么知道委托是异步的呢?为什么 C# 允许我将返回 Task 的方法分配给应该返回 void 的操作变量?

任何帮助将不胜感激!

[Fact]
public async Task CanCatchExceptions() {
    var worker = new Worker {
        WorkAction = async item => await Throws()
    };
    worker.DoWork(new WorkItem { Id = 1 });
}
public class Worker {
    // would prefer to keep work action simple and not require a Task.
    public Action<WorkItem> WorkAction { get; set; }
    public void DoWork(WorkItem item) {
        try {
            WorkAction(item);
        } catch {
            Debug.WriteLine("Handled");
        }
    }
}
public class WorkItem {
    public int Id { get; set; }
}
public async Task Throws() {
    throw new ApplicationException();
}

使用异步委托进行异常处理

没有返回值的 Lambda 可以强制转换为任务返回方法(例如,Func<Task>)或 void 返回方法(例如,Action )。请注意,当强制转换为 void 返回方法时,该 lambda 的实际方法是一种async void方法,async void方法附带的所有问题。我在最佳实践文章中描述了async void的一些问题;其中之一是您无法捕获异常 try .

最好的选择是更改工作项以返回Task,这是异步方法的更自然的表示形式。

为什么 C# 允许我将返回 Task 的方法分配给应该返回 void 的操作变量?

由于async void是异步事件处理程序的合法用例,因此编译器允许 void 返回Action<T>并且不抱怨。一旦您意识到这一事实,您就可以显式使用Func<Task>,这将创建所需的Task返回方法重载。

正如您所说,您希望保持工作操作"简单"并且不需要任务,请考虑提供异步重载,该重载将与异步控制流一起正常运行:

public Func<WorkItem, Task> WorkAsync { get; set; }
public async Task WorkAsync(WorkItem item)
{
      try
      {
            await WorkAsync(item)
      }
      catch (Exception e)
      {
           // Handle
      }
}

请注意,根据您的选择,这可能属于完全不同的类(将异步工作线程与同步工作线程分开)。