C#Building Fluent API用于方法调用

时间:2011-06-07 16:30:00

标签: c# fluent-interface

我需要做什么才能说 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;
    }
}

5 个答案:

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

请注意,RepeatCountAction未在界面上公开。这样,在调用.Invoke(x => x.时,您将看不到这些成员,但在ExecutionBuilder方法中使用具体的Invoke()类时,可以访问它们。

答案 3 :(得分:1)

您可以拥有SetInvokeMethodExecute方法。

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