我需要做什么才能说 InvokeMethod 可以调用某个方法,当使用重复这样的特殊选项时,它应该在重复之后执行>
我现在的问题是该方法在知道必须被调用100次之前已经执行了。
class Program
{
static void Main()
{
const bool shouldRun = true;
new MethodExecuter()
.ForAllInvocationsUseCondition(!Context.WannaShutDown)
.InvokeMethod(A.Process).Repeat(100)
.When(shouldRun).ThenInvokeMethod(B.Process).Repeat(10)
.ForAllInvocationsUseCondition(Context.WannaShutDown)
.When(shouldRun).ThenInvokeMethod(C.Process);
}
}
MethodExpression的
public class MethodExpression
{
private bool _isTrue = true;
private readonly MethodExecuter _methodExecuter;
public MethodExpression(bool isTrue, MethodExecuter methodExecuter)
{
_isTrue = isTrue;
_methodExecuter = methodExecuter;
}
public MethodExecuter ThenInvokeMethod(Action action)
{
if (_isTrue)
{
action.Invoke();
_isTrue = false;
}
return _methodExecuter;
}
}
MethodExecuter
public class MethodExecuter
{
private bool _condition;
private int _repeat = 1;
public MethodExpression When(bool isTrue)
{
return new MethodExpression(isTrue && _condition, this);
}
public MethodExecuter InvokeMethod(Action action)
{
if (_condition)
{
for (int i = 1; i <= _repeat; i++)
{
action.Invoke();
}
}
return this;
}
public MethodExecuter ForAllInvocationsUseCondition(bool condition)
{
_condition = condition;
return this;
}
public MethodExecuter Repeat(int repeat)
{
_repeat = repeat;
return this;
}
}
答案 0 :(得分:3)
您提供的内容有点像编程工作流程或状态机。为了在执行期间捕获调用和尊重条件,您需要稍微改变您的方法。
不要在进入时调用操作,而是考虑将操作推送到队列中,然后提供运行状态机的机制。
new MethodInvoker()
.ForAllInvocationsUseCondition(true)
.InvokeMethod( Process.A ).Repeat(100)
.Run();
答案 1 :(得分:3)
使用最终方法(“go”或“execute”)来实际开始。
new MethodExecuter()
.ForAllInvocationsUseCondition(!Context.WannaShutDown)
.InvokeMethod(A.Process).Repeat(100)
.When(shouldRun).ThenInvokeMethod(B.Process).Repeat(10)
.ForAllInvocationsUseCondition(Context.WannaShutDown)
.When(shouldRun).ThenInvokeMethod(C.Process)
.Go();
答案 2 :(得分:2)
有很多方法可以对这只猫进行修饰,但我认为这种困难的一个原因是你实际上在InvokeMethod()
方法中调用了这个方法(如图!)。
通常,我们使用流畅的API将从内到外评估的语法转换为可以从左到右的方式表达的内容。因此,接口的表达式构建器组件用于在整个表达式中构建状态,并且仅在最后才发生“实际工作”。
您当前问题的一个解决方案是使用其相关选项(调用条件,重复计数等)对每个操作进行排队,并向ExecuteAll()
添加一些MethodExecuter
方法,该方法出列并执行在成员链末尾完全配置的操作。
另一种解决方案是将所有执行选项放在InvokeMethod()
方法中;类似的东西:
.Invoke(x => x.Method(A.Process).Repeat(100))
此方法类似于:
public MethodExecuter Invoke(Action<IExecutionBuilder> executionBuilder)
{
var builder = new ExecutionBuilder();
executionBuilder(builder);
var action = builder.Action;
var repeat = builder.RepeatCount;
if (_condition)
{
for (int i = 1; i <= repeat; i++)
{
action();
}
}
return this;
}
我没有在Visual Studio中完成这项工作,但其他项目将是:
public interface IExecutionBuilder
{
IExecutionBuilder Method(Action action);
IExecutionBuilder Repeat(int count);
}
public class ExecutionBuilder : IExecutionBuilder
{
public ExecutionBuilder()
{
RepeatCount = 1; // default to repeat once
Action = () => {}; // default to do nothing, but not null
}
public IExecutionBuilder Method(Action action)
{
Action = action;
return this;
}
public IExecutionBuilder Repeat(int repeat)
{
RepeatCount = repeat;
return this;
}
public int RepeatCount { get; private set; }
public Action Action { get; private set; }
}
请注意,RepeatCount
和Action
未在界面上公开。这样,在调用.Invoke(x => x.
时,您将看不到这些成员,但在ExecutionBuilder
方法中使用具体的Invoke()
类时,可以访问它们。
答案 3 :(得分:1)
您可以拥有SetInvokeMethod
和Execute
方法。
SetInvokeMethod(Action).Repeat(100).Execute()
答案 4 :(得分:0)
在一个句子中,你的代码太“急切”。在您的流利语法结构告诉您的代码应该重复多少次之前,调用InvokeMethod方法并执行操作。
要更改此设置,请尝试在methodInvoker中指定要作为私有字段调用的方法,然后包含一个命令,该命令是实际执行命令的“触发器”。关键是“懒惰的评价”;在流畅的界面中,除非必须,否则不应该做任何事情;这样你就可以控制它何时发生。
public class FluentMethodInvoker
{
Predicate condition = ()=>true;
Predicate allCondition = null;
Action method = ()=> {return;};
bool iterations = 1;
FluentMethodInvoker previous = null;
public FluentMethodInvoker(){}
private FluentMethodInvoker(FluentMethodInvoker prevNode)
{ previous = prevNode; }
public FluentMethodInvoker InvokeMethod(Action action)
{
method = action;
}
//Changed "When" to "If"; the function does not wait for the condition to be true
public FluentMethodInvoker If(Predicate pred)
{
condition = pred;
return this;
}
public FluentMethodInvoker ForAllIf(Predicate pred)
{
allCondition = pred;
return this;
}
private bool CheckAllIf()
{
return allCondition == null
? previous == null
? true
: previous.CheckAllIf();
: allCondition;
}
public FluentMethodInvoker Repeat(int repetitions)
{
iterations = repetitions;
return this;
}
//Merging MethodExecuter and MethodExpression, by chaining instances of FluentMethodInvoker
public FluentMethodInvoker Then()
{
return new FluentMethodInvoker(this);
}
//Here's your trigger
public void Run()
{
//goes backward through the chain to the starting node
if(previous != null) previous.Run();
if(condition && CheckAllIf())
for(var i=0; i<repetitions; i++)
method();
return;
}
}
//usage
class Program
{
static void Main()
{
const bool shouldRun = true;
var invokerChain = new FluentMethodInvoker()
.ForAllIf(!Context.WannaShutDown)
.InvokeMethod(A.Process).Repeat(100)
.When(shouldRun)
.Then().InvokeMethod(B.Process).Repeat(10)
.ForAllIf(Context.WannaShutDown)
.When(shouldRun)
.Then().InvokeMethod(C.Process);
//to illustrate that the chain doesn't have to execute immediately when being built
invokerChain.Run();
}
}