im使用ExcecuteWithLogging方法重构了一些try catch块,但是restHandler.GetResultAsync()中发生的异常却从未被捕获,为什么?
public async Task<Aaa> SearchByPersonnummerAsync(string personnummer)
{
var restHandler = new RestHandler();
GenericResult<Aaa> aaa= await ExcecuteWithLogging(async () =>
{
var res = await restHandler.GetResultAsync<Aaa>(
_configurationManager.GetSetting("api"),
"url");
return res;
});
return aaa.Result;
}
private T ExcecuteWithLogging<T>(Func<T> function)
{
try
{
function();
}
catch (Exception ex)
{
var message = ex.Message;
// Log
}
return default(T);
}
答案 0 :(得分:0)
这是我喜欢的解决方案,
var aaa = null;
await ExcecuteWithLogging(async () =>
aaa = await Method());
public async Task<string> ExcecuteWithLogging(Func<Task> action)
{
try
{
await action();
}
catch (Exception ex)
{
// handle exception
return null;
}
return "ok";
}
答案 1 :(得分:0)
我遇到了类似的问题,这是我的解决方案。就我而言,我不能确定函数的类型,所以我只是为异步函数创建了一个重载:
public static T CallAndLogErrors<T, TLog>(Func<T> method, ILogger<TLog> logger)
{
try
{
return method();
}
catch (Exception e)
{
LogError(e, logger);
throw;
}
}
public static async Task<T> CallAndLogErrors<T, TLog>(Func<Task<T>> method, ILogger<TLog> logger)
{
try
{
return await method();
}
catch (Exception e)
{
LogError(e, logger);
throw;
}
}
这使得可以使用T
,Task<T>
或async Task<T>
返回类型调用方法,例如:
var result = CallAndLogErrors(() => Method(), logger);
var result = await CallAndLogErrors(() => Method(), logger);
所有异常都被捕获。
答案 2 :(得分:-1)
问题在于您没有实现ExecuteWithLogging
的重载,该重载可以为Func<Task<T>>
专门解决 。请记住,lambda表达式可以转换为任何兼容的委托,这就是为什么您将异步lambda成功分配给当前ExecuteWithLogging
方法的原因。
在阅读其余答案之前,请先阅读以下article,当我遇到与您现在面临的完全相同的问题时,它对我有很大帮助。我非常确定,在阅读了该文章之后,您将了解这里发生的事情,但是为了完整起见,这里有一些示例可以使事情变得更加清晰。我已经通过以下方式修改了您的方法:
public static async void SearchByPersonnummerAsync(string personnummer)
{
var aaa = await ExcecuteWithLogging(async () =>
{
Console.WriteLine("Executing function");
var res = await Task.Run(() =>
{
Thread.Sleep(100);
Console.WriteLine("Before crashing");
throw new Exception();
return 1;
});
Console.WriteLine("Finishing execution");
return res;
});
}
private static T ExcecuteWithLogging<T>(Func<T> function)
{
try
{
Console.WriteLine("Before calling function");
function();
Console.WriteLine("After calling function");
}
catch (Exception ex)
{
var message = ex.Message;
Console.WriteLine(message);
}
Console.WriteLine("Returning..");
return default(T);
}
这就是您现在的位置,我只添加了一些控制台日志记录代码,输出是什么?
您可以看到ExecutingWithLogging
正在返回,而异步委托甚至没有崩溃!
让我们添加一个接受Func<Task<T>>
private static async Task<T> ExcecuteWithLogging<T>(Func<Task<T>> function)
{
T result;
try
{
Console.WriteLine("Before calling function");
result = await function();
Console.WriteLine("After calling function");
}
catch (Exception ex)
{
var message = ex.Message;
Console.WriteLine(message);
return default(T);
}
Console.WriteLine("Returning..");
return result;
}
编译器将立即从上面提到的文章中进行选择:
编译器更喜欢期望返回任务的委托的方法调用。很好,因为您要使用它。编译器使用有关匿名委托的返回值的类型推断规则得出此结论。任何异步匿名函数的“推断的返回类型”都假定为任务。知道由lambda表示的匿名函数会返回一个Task,则具有Func参数的Task.Run()的重载是更好的匹配。
现在输出为:
您就被抓住了。那么道德是什么?再次,我引用提到的文章:
首先,避免将异步lambda用作期望Action的方法的参数,并且不要提供期望Func的重载。如果这样做,您将创建一个异步void lambda。编译器会很高兴地认为这就是您想要的。
第二,如果您编写将委托作为参数的方法, 考虑程序员是否希望使用异步lambda那样 论点。如果是这样,请创建一个使用Func作为 除了Action之外的参数。作为必然结果,创建一个重载 除了Task之外,还需要Func>作为参数 返回一个值。
编辑
正如@Servy在评论中指出的那样,如果仅保留ExecuteWithLogging
的原始版本T实际上是Task<Aaa>
...但是我能想到的没有过载的唯一方法是:
private static async Task<T> ExcecuteWithLogging<T>(Func<T> function)
{
try
{
var result = function();
if (result is Task t)
{
return await t;
}
return result;
}
catch (Exception ex)
{
var message = ex.Message;
Console.WriteLine(message);
}
Console.WriteLine("Returning..");
return default(T);
}
但是,除了丑陋的恕我直言外,它总是会失败,因为对function()
的调用是同步运行的,并且在您要等待的时间,任务已经结束或崩溃。但是这种方法最糟糕的部分是它甚至无法编译,编译器抱怨道:无法将void类型隐式转换为T