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