我需要重试某种方法,直到它返回非空的Guid。
有一个很棒的answer会根据是否存在异常重试;但是,我想将此类通用化,以便能够处理任何指定的条件。
当前用法将执行特定次数的操作,直到没有例外:
Retry.Do(() => SomeFunctionThatCanFail(), TimeSpan.FromSeconds(1));
要么:
Retry.Do(SomeFunctionThatCanFail, TimeSpan.FromSeconds(1));
要么:
int result = Retry.Do(SomeFunctionWhichReturnsInt, TimeSpan.FromSeconds(1), 4);
如何修改此类,使其根据传入的函数的返回值重试?
例如,如果我想重试直到函数返回3:
Retry.Do(() => SomeFunctionThatCanFail(), TimeSpan.FromSeconds(1)).Until(3);
这意味着每隔1秒执行SomeFunctionThatCanFail()直到SomeFunctionThatCanFail()= 3?
在满足条件之前,我如何概括Retry.Do的用法?
public static class Retry
{
public static void Do(
Action action,
TimeSpan retryInterval,
int retryCount = 3)
{
Do<object>(() =>
{
action();
return null;
}, retryInterval, retryCount);
}
public static T Do<T>(
Func<T> action,
TimeSpan retryInterval,
int retryCount = 3)
{
var exceptions = new List<Exception>();
for (int retry = 0; retry < retryCount; retry++) //I would like to change this logic so that it will retry not based on whether there is an exception but based on the return value of Action
{
try
{
if (retry > 0)
Thread.Sleep(retryInterval);
return action();
}
catch (Exception ex)
{
exceptions.Add(ex);
}
}
throw new AggregateException(exceptions);
}
}
参考方案
如何创建以下界面:
public interface IRetryCondition<TResult>
{
TResult Until(Func<TResult, bool> condition);
}
public class RetryCondition<TResult> : IRetryCondition<TResult>
{
private TResult _value;
private Func<IRetryCondition<TResult>> _retry;
public RetryCondition(TResult value, Func<IRetryCondition<TResult>> retry)
{
_value = value;
_retry = retry;
}
public TResult Until(Func<TResult, bool> condition)
{
return condition(_value) ? _value : _retry().Until(condition);
}
}
然后,您将更新您的Retry
静态类:
public static class Retry
{
// This method stays the same
// Returning an IRetryCondition does not make sense in a "void" action
public static void Do(
Action action,
TimeSpan retryInterval,
int retryCount = 3)
{
Do<object>(() =>
{
action();
return null;
}, retryInterval, retryCount);
}
// Return an IRetryCondition<T> instance
public static IRetryCondition<T> Do<T>(
Func<T> action,
TimeSpan retryInterval,
int retryCount = 3)
{
var exceptions = new List<Exception>();
for (int retry = 0; retry < retryCount; retry++)
{
try
{
if (retry > 0)
Thread.Sleep(retryInterval);
// We return a retry condition loaded with the return value of action() and telling it to execute this same method again if condition is not met.
return new RetryCondition<T>(action(), () => Do(action, retryInterval, retryCount));
}
catch (Exception ex)
{
exceptions.Add(ex);
}
}
throw new AggregateException(exceptions);
}
}
您将能够实现以下目标:
int result = Retry.Do(() => SomeFunctionThatCanFail(), TimeSpan.FromSeconds(1)).Until(r => r == 3);
更实用的方法
我试图提出一个更加“面向功能”的解决方案(有点类似于LINQ):
首先,我们将有两个接口来执行操作:
public interface IRetryResult
{
void Execute();
}
public interface IRetryResult<out TResult>
{
TResult Execute();
}
然后,我们需要两个接口来配置重试操作:
public interface IRetryConfiguration : IRetryResult
{
IRetryConfiguration Times(int times);
IRetryConfiguration Interval(TimeSpan interval);
}
public interface IRetryConfiguration<out TResult> : IRetryResult<TResult>
{
IRetryConfiguration<TResult> Times(int times);
IRetryConfiguration<TResult> Interval(TimeSpan interval);
IRetryConfiguration<TResult> Until(Function<TResult, bool> condition);
}
最后,两个接口都需要两种实现:
public class ActionRetryConfiguration : IRetryConfiguration
{
private readonly Action _action;
private readonly int? _times;
private readonly TimeSpan? _interval;
public ActionRetryConfiguration(Action action, int? times, TimeSpan? interval)
{
_action = action;
_times = times;
_interval = interval;
}
public void Execute()
{
Execute(_action, _times, _interval);
}
private void Execute(Action action, int? times, TimeSpan? interval)
{
action();
if (times.HasValue && times.Value <= 1) return;
if (times.HasValue && interval.HasValue) Thread.Sleep(interval.Value);
Execute(action, times - 1, interval);
}
public IRetryConfiguration Times(int times)
{
return new ActionRetryConfiguration(_action, times, _interval);
}
public IRetryConfiguration Interval(TimeSpan interval)
{
return new ActionRetryConfiguration(_action, _times, interval);
}
}
public class FunctionRetryConfiguration<TResult> : IRetryConfiguration<TResult>
{
private readonly Func<TResult> _function;
private readonly int? _times;
private readonly TimeSpan? _interval;
private readonly Func<TResult, bool> _condition;
public FunctionRetryConfiguration(Func<TResult> function, int? times, TimeSpan? interval, Func<TResult, bool> condition)
{
_function = function;
_times = times;
_interval = interval;
_condition = condition;
}
public TResult Execute()
{
return Execute(_function, _times, _interval, _condition);
}
private TResult Execute(Func<TResult> function, int? times, TimeSpan? interval, Func<TResult, bool> condition)
{
TResult result = function();
if (condition != null && condition(result)) return result;
if (times.HasValue && times.Value <= 1) return result;
if ((times.HasValue || condition != null) && interval.HasValue) Thread.Sleep(interval.Value);
return Execute(function, times - 1, interval, condition);
}
public IRetryConfiguration<TResult> Times(int times)
{
return new FunctionRetryConfiguration<TResult>(_function, times, _interval, _condition);
}
public IRetryConfiguration<TResult> Interval(TimeSpan interval)
{
return new FunctionRetryConfiguration<TResult>(_function, _times, interval, _condition);
}
public IRetryConfiguration<TResult> Until(Func<TResult, bool> condition)
{
return new FunctionRetryConfiguration<TResult>(_function, _times, _interval, condition);
}
}
最后,Retry
静态类,入口点:
public static class Retry
{
public static IRetryConfiguration Do(Action action)
{
return new ActionRetryConfiguration(action, 1, null);
}
public static IRetryConfiguration<TResult> Do<TResult>(Func<TResult> action)
{
return new FunctionRetryConfiguration<TResult>(action, 1, null, null);
}
}
我认为这种方法比较容易出错,而且更清洁。
此外,它还允许您执行以下操作:
int result = Retry.Do(SomeIntMethod).Interval(TimeSpan.FromSeconds(1)).Until(n => n > 20).Execute();
Retry.Do(SomeVoidMethod).Times(4).Execute();
Java中的<<或>>>是什么意思? - javaThis question already has answers here: Closed 7 years ago. Possible Duplicate: What does >> and >>> mean in Java?我在一些Java代码中遇到了一些陌生的符号,尽管代码可以正确编译和运行,但对于括号在此代码中的作用却感…
菱形运算符<>是否等于<?> - java我在util.TreeSet类中发现,其中一个构造函数正在使用具有空泛型类型的新TreeMap调用另一个构造函数。 public TreeSet(Comparator<? super E> comparator) { this(new TreeMap<>(comparator)); } new TreeMap<>是什么意思…
声纳测试用例失败 - java我正在尝试为我的项目之一获得声纳报告。我已经运行mvn clean installRunning blah.blah.BlahTest1 Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.016 sec Running blah.blah.BlahTest2 Tests run…
休眠映射<键,设置<值>> - java我有以下表格:@Entity @Table(name = "events") Event --id --name @Entity @Table(name = "state") State --id --name @Entity @Table(name = "action") Action --id …
如何使用BeautifulSoup在<tr>中捕获特定的<td> - python尝试从nyc Wiki页面中的高中列表中获取所有高中名称。我已经写了足够多的脚本,可以让我获取包含在高中,学业和入学条件列表的表的<tr>标记中的所有信息-但是我如何才能缩小到我认为的范围内在td[0]内休息(会弹出KeyError)-只是学校的名称?到目前为止我写的代码:from bs4 import BeautifulSoup from ur…