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

时间:2014-11-28 19:44:37

标签: c# delegates async-await

我想知道我在这里做错了什么。我确定它有些愚蠢,但我可以用一些额外的眼睛来弄清楚它是什么。我正在尝试创建一个可以分配工作操作委托的工作服务。当我尝试捕获工作操作委托抛出的异常时,不会调用处理程序。我认为这是因为我的动作实际上是一个返回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();
}

2 个答案:

答案 0 :(得分:4)

没有返回值的Lambda可以转换为任务返回方法(例如,Func<Task>)或返回空白的方法(例如,Action)。请注意,在转换为void返回方法时,该lambda的实际方法是async void方法,其中包含async void方法带来的所有问题。我在best practices article中描述了async void的一些问题;其中之一是您无法通过try捕获异常。

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

答案 1 :(得分:1)

  

为什么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
      }
}

请注意,这可能是一个完全不同的类(根据您的选择将异步工作者与同步工作者分开)。