我在这里有一些代码。这是真实类的简化版本:
public class Delayer
{
//it has to be unawaitable
public async void Execute(Action action)
{
await Task.Delay(10).ConfigureAwait(false);
action.BeginInvoke(null, null); //action.Invoke();
}
}
我用它:
private static Task TestFoo()
{
throw new Exception();
}
delayer.Execute(async () =>
{
//do something else
await TestFoo().ConfigureAwait(false);
});
我无法通过将Execute
方法传递到try / catch中来解决此异常,我也无法将action.BeginInvoke(null, null)
传递给try / catch。如果我将try / catch传递给Execute
方法时,我可以处理异步lambda。
我的问题是:为什么异步lambda用await执行?因为如果没有等待执行,则会吞下异常。
我希望Execute
方法吞下从操作中抛出的所有异常。有什么想法怎么做?我做错了什么?
增加:
Execute
的行为必须像"只是火灾和遗忘操作"。
答案 0 :(得分:1)
修改强>
如果你真的,真的想要Fire和Forget方法,唯一要做的就是
捕获Execute方法中的所有异常。但是,如果您希望能够捕获异常而不是在非等待的BeginInvoke
上使用Action
,则必须接受一项等待审核的任务。
public class Delayer
{
public async Task Execute(Func<Task> action) // or public async void Execute(Func<Task> action) if you insist on it.
{
try
{
await Task.Delay(10).ConfigureAwait(false);
await action.Invoke();
}
catch(Exception ex)
{
Console.WriteLine(ex);
}
}
}
然后你就可以安全地做到了
void CallDelayedMethod()
{
var delayer = new Delayer();
delayer.Execute(ThrowException);
}
public Task ThrowException()
{
throw new Exception();
}
我仍然会返回一个任务并留给调用者忽略它,不等待它(火与忘记)。
原始回答
您没有在类Delayer
中使用异步void签名来遵循最佳做法。
public async void Execute(Action action)
应该是
public async Task Execute(Action action)
所以你可以等待Execute
的电话。否则它只是一个火灾和忘记操作,这使得捕捉异常变得困难。通过将其设为awaitbale,您可以:
try
{
await Delayer.Execute(...);
}
catch(Exception ex)
{
....
}
Async void方法具有不同的错误处理语义。当异步任务或异步任务方法抛出异常时,将捕获该异常并将其放在Task对象上。使用async void方法时,没有Task对象,因此异步void方法抛出的任何异常都将直接在async void方法启动时处于活动状态的SynchronizationContext上引发。
此外,如果要向其传递可接受的操作,则应执行“执行接受任务”:
public async Task Execute(Func<Task> action)
{
await Task.Delay(10).ConfigureAwait(false);
await action.Invoke();
}