C # Building Fluent API for calling methods

What I need to do to say that InvokeMethod can call the method and when using special options such as Repeat , it should exexute after Repeat .

My problem at the moment is that the method will already be exexute before it finds out that it should be called 100 times.

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; } } 
+7
source share
5 answers

There are many ways to hide this cat, but I think one of the sources of this difficulty is that you actually call the method in the InvokeMethod() (go figure!) Method.

Typically, we use free APIs to convert syntax that evaluates from the inside out to something that can be expressed in order from left to right. Thus, the components of the interface expression builder are used to create state in the entire expression, and only at the end does "real work" happen.

One solution to your immediate problem is to queue each action with its associated parameters (call conditions, number of retries, etc.) and add the ExecuteAll() method to MethodExecuter , which cancels and performs fully configured actions at the end of the member chain.

Another solution would be to put all the execution options inside the InvokeMethod() method; something like:

 .Invoke(x => x.Method(A.Process).Repeat(100)) 

This method will look something like this:

 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; } 

I did not work with this in Visual Studio, but other elements would be like this:

 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; } } 

Please note that RepeatCount and Action not displayed on the interface. That way, you won’t see these members when you call .Invoke(x => x. , But you can access them when using the specific ExecutionBuilder class inside the Invoke() method.

+2
source

What you provided is a bit like programming a workflow or state machine. To capture the challenges and comply with the conditions at runtime, you need to slightly change your approach.

Instead of invoking actions as they arrive, think about pushing your actions into the queue, and then providing a mechanism to trigger the state machine.

 new MethodInvoker() .ForAllInvocationsUseCondition(true) .InvokeMethod( Process.A ).Repeat(100) .Run(); 
+3
source

Use the final method ("go" or "execute") to actually disable the game.

  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(); 
+3
source

You may have a SetInvokeMethod and Execute method.

 SetInvokeMethod(Action).Repeat(100).Execute() 
+1
source

In the sentence, your code is too impatient. The InvokeMethod method is called and takes an action before your free grammar structure tells your code how many times it should repeat.

To change this, try also specifying the method that you call as a private field in your Invoker method, and then include the command, which is the β€œtrigger” for the actual execution of the commands. The key is "lazy appreciation"; nothing should be done in the fluent interface until this happens; that way you have most of the control over when this will happen.

 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 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(); } } 
0
source

All Articles