在同步世界中,我有一个TryExecute函数来包装try / catch / log逻辑以供重用,如下所示:
TryExecute(() => SyncFunction());
private static void TryExecute(Action action)
{
try
{
action();
}
catch (Exception ex)
{
Log(ex);
throw;
}
}
我不知道如何将其重写为异步/等待模式。
据我了解,我有五种有效的方法将其重写为async / await(忽略任何其他Visual Studio发出的警告)。
将原始同步TryExecute()
与异步委托一起使用:
(1) TryExecute(async () => await AsyncFunction());
似乎不再等待,TryExecute()
通过而没有等待AsyncFunction()
完成。
重写为新的同步TryExecuteTask()
返回Task,在有或没有异步委托的情况下调用它:
(2) await TryExecuteTask(() => AsyncFunction());
(3) await TryExecuteTask(async () => await AsyncFunction());
private static Task TryExecuteTask(Func<Task> asyncAction)
{
try
{
return asyncAction();
}
catch (Exception ex)
{
Log(ex);
throw;
}
}
或重写为新的异步TryExecuteAsync()
,在有或没有异步委托的情况下调用它:
(4) await TryExecuteAsync(() => AsyncFunction());
(5) await TryExecuteAsync(async () => await AsyncFunction());
private async static Task TryExecuteAsync(Func<Task> asyncAction)
{
try
{
await asyncAction();
}
catch (Exception ex)
{
Log(ex);
throw;
}
}
但是,如果我从Exception
内部扔出AsyncFunction()
,那么以上五种方式都无法抓住Exception
。所有操作都停止,但未处理异常。只捕获没有委托的作品:
(0) try
{
await AsyncFunction();
}
catch (Exception ex)
{
Log(ex);
}
这意味着我不能使用(1)到(5)中的任何形式的TryExecute()
来重用try / catch / log逻辑,我只能在(0)之类的任何地方重复try / catch / log
我的整个控制台代码如下:
class Program
{
async static Task Main(string[] args)
{
// Original sync way
TryExecute(() => SyncFunction());
Console.WriteLine("0");
try
{
await AsyncFunction();
}
catch (Exception ex)
{
Log(ex);
}
////Console.WriteLine("1");
////TryExecute(async () => await AsyncFunction());
////Console.WriteLine("2");
////await TryExecuteTask(() => AsyncFunction());
////Console.WriteLine("3");
////await TryExecuteTask(async () => await AsyncFunction());
////Console.WriteLine("4");
////await TryExecuteAsync(() => AsyncFunction());
////Console.WriteLine("5");
////await TryExecuteAsync(async () => await AsyncFunction());
Console.WriteLine("Finished without unhandled exception.");
}
private static void SyncFunction()
{
Console.WriteLine("SyncFunction starting");
Thread.Sleep(500);
Console.WriteLine("SyncFunction starting");
throw new Exception();
}
private async static Task AsyncFunction()
{
Console.WriteLine("AsyncFunction starting");
await Task.Run(() =>
{
Console.WriteLine("Sleep starting");
Thread.Sleep(500);
Console.WriteLine("Sleep end");
throw new Exception();
});
Console.WriteLine("AsyncFunction end");
}
private static void TryExecute(Action action)
{
try
{
action();
}
catch (Exception ex)
{
Log(ex);
}
}
private static Task TryExecuteTask(Func<Task> asyncAction)
{
try
{
return asyncAction();
}
catch (Exception ex)
{
Log(ex);
throw;
}
}
private async static Task TryExecuteAsync(Func<Task> asyncAction)
{
try
{
await asyncAction();
}
catch (Exception ex)
{
Log(ex);
throw;
}
}
private static void Log(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
由于未处理的异常,我只能注释掉除Main()
以外的所有部分,以测试每种情况。
答案 0 :(得分:1)
呼叫await TryExecuteAsync(AsyncFunction)
的工作方式与您期望的一样:
class Program
{
async static Task Main(string[] args)
{
await TryExecuteAsync(AsyncFunction);
Console.WriteLine("Finished without unhandled exception.");
}
private async static Task AsyncFunction()
{
Console.WriteLine("AsyncFunction starting");
await Task.Run(() =>
{
Console.WriteLine("Sleep starting");
Thread.Sleep(3000);
Console.WriteLine("Sleep end");
throw new Exception();
});
Console.WriteLine("AsyncFunction end");
}
private async static Task TryExecuteAsync(Func<Task> asyncAction)
{
try
{
await asyncAction();
}
catch (Exception ex)
{
Log(ex);
throw;
}
}
private static void Log(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
AsyncFunction()
引发一个异常,该异常已记录,然后在TryExecuteAsync
中重新抛出。如果要捕获重新引发的异常,则应在对try/catch
的调用周围放置TryExecuteAsync
:
async static Task Main(string[] args)
{
try
{
await TryExecuteAsync(AsyncFunction);
Console.WriteLine("Finished without unhandled exception.");
}
catch (Exception ex)
{
Console.WriteLine("Failed to execute: " + ex.Message);
}
}
答案 1 :(得分:0)
我不知道如何将其重写为异步/等待模式。
转换为async
时,第一步是转换方法调用。在这种情况下,应首先将委托转换为异步兼容的委托。
Action
是一个不带参数且没有返回值的委托,例如void Method()
。没有参数且没有返回值的异步方法看起来像async Task Method()
,所以its delegate type would be Func<Task>
。
旁注:与代表打交道时要记住async void
is unnatural and should be avoided。
一旦您将委托类型从Action
更改为Func<Task>
,就可以await
返回值,这将使您的TryExecute
方法更改为{{1} },例如:
async Task
以上五种方法均不能捕获Exception。所有操作都因未处理的异常而停止。
这实际上只是在调试器中运行代码的副作用。使用异步代码,有时您会看到实际上未处理的“未处理”异常。这是因为是由编译器生成的代码正在捕获异常并将其放置在任务上,稍后您的代码private static async Task TryExecuteAsync(Func<Task> asyncAction)
{
try
{
await asyncAction();
}
catch (Exception ex)
{
Log(ex);
throw;
}
}
将其重新引发,然后代码await
将其重新引发。当原始异常被您的代码以外的其他东西捕获(由编译器生成的代码捕获)时,调试器会感到有些惊讶,并且无法知道这是完全正常的。>
因此,如果您继续跳过调试器的“未处理”异常,您会发现它工作得很好。