以此代码为例 - 我认为当显示TException时,我应该能够“捕获”它并重试我的func()
适当的次数。但是当我把这些代码放在野外时,即使抛出TException类型的异常,它也会跳过catch子句并冒泡。有人可以解释原因吗?
public static T TryNTimes<T, TException>(Func<T> func, int times) where TException : Exception
{
if (times <= 0)
throw new ArgumentException($"TryNTimes: `times` must be a positive integer. You passed in: {times}");
while (times > 0)
{
try
{
return func();
}
catch (TException)
{
if (--times <= 0)
throw;
}
}
// should never reach here
return default(T);
}
代码被调用如下:
await RetryUtils.TryNTimes<Task, MyCustomException>(
() => TryHandleElasticMappingError(dataGridResults, dataGridRecords),
MyFieldsCount)
.ConfigureAwait(false);
这是一个异步问题吗?上面的行包含在Try-Catch中,它捕获了Exception
,我能够验证异常的类型是MyCustomException
。我可以确认内部catch块(重试方法中的那个)永远不会被击中。
我认为你的问题是由于你的func
是异步的。我会尝试这样的异步func
s
using System;
using System.Threading.Tasks;
namespace GenericException
{
public static class FuncHelpers
{
public static async Task<T> TryNTimesAsync<T,TException>(this Func<Task<T>> func, int n) where TException : Exception
{
while(n --> 0)
{
try
{
return await func();
}
catch(TException)
{
if(n < 0)
throw;
}
}
return default(T);
}
public static async Task TryNTimesAsync<TException>(this Func<Task> func, int n) where TException : Exception
{
while(n --> 0)
{
try
{
await func();
}
catch(TException)
{
if(n <= 0) throw;
}
}
}
public static T TryNTimes<T,TException>(this Func<T> func, int n) where TException : Exception
{
while(n --> 0)
{
try
{
return func();
}
catch(TException)
{
if(n <= 0) throw;
}
}
return default(T);
}
}
class Program
{
static Task AddTwoNumbersAsync(int num1, int num2)
{
var task = new Task(() =>
{
if(num1 % 2 == 0) throw new ArgumentException($"{nameof(num1)} must be odd");
Console.WriteLine($"{num1} + {num2} = {num1 + num2}");
});
task.Start();
return task;
}
static async Task Main(string[] args)
{
Func<Task> addTask = () => AddTwoNumbersAsync(2, 4);
Console.WriteLine("trying to add non async");
var taskResult = addTask.TryNTimes<Task, ArgumentException>(5);
Console.WriteLine("trying to add async");
try
{
await addTask.TryNTimesAsync<ArgumentException>(5);
}
catch(ArgumentException)
{
Console.WriteLine("There was an error trying to add async");
}
}
}
}
至于为什么它不按预期工作,我同意@Hans Kilian。
基本上,你的func
正在返回一个Task
,或承诺做某事。那个东西在Task
完成之前并不一定完成。您在此方法之外捕获异常的原因是当您调用.ConfigureAwait(false)
时,您正在等待Task
完成,因此它将抛出异常。
编辑:完整示例代码,包括Task
与Task<T>
的异步运行程序
这是一个异步问题吗?
如其他答案中所述,是的,这是一个异步问题。
C#中的异步和迭代器块是协同程序。正常的例程可以做三件事:运行完成,抛出或进入无限循环。一个协程可以做第四件事:暂停,以后再恢复。 await
是异步区块中发生暂停的点;它是迭代器块中的yield return
。
在你的情况下,直到协程恢复,投掷才会发生,此时try
不再生效; try
的方法运行完成,因为它不是协程。如果你想让try
生效,那么try也必须在异步块中,你必须在try中使用await
。
同样,如果你写道:
IEnumerable<int> Weird(bool b)
{
if (b) throw new Exception();
yield return 1;
}
...
IEnumerable<int> x = null;
try
{
x = Weird(true); // Should throw, right?
}
catch(Exception ex)
{
// Nope; this is unreachable
}
foreach(int y in x) // the throw happens here!
迭代器块协程开始挂起,并且在迭代器上调用MoveNext
之前不会恢复;它只是返回一个迭代器。异步块协程在await上挂起,但不需要在await上挂起;等待已经完成的任务被允许跳过暂停。
使用协同程序尝试捕获是棘手的;小心!