【问题标题】:How create Fluent Interface in C# with some limitation for some methods?如何在 C# 中创建 Fluent Interface,但对某些方法有一些限制?
【发布时间】:2016-07-18 05:13:24
【问题描述】:

见以下代码:

new ConditionCreator()
       .Add()
             .Or()
       .Add()
             .And()
       .Add()

我想为此创建一个流畅的界面 但是我需要, 在 Add() 方法开发人员之后只能看到 Or() 或 And() 和 在其中之一之后,请参见 Only Add() 方法。

所以没有人能写出这样的代码:

new ConditionCreator()
           .Add()
           .Add()
           .Add()
           .Or()
           .And()
           .Add()
           .And()
           .And()

我想限制某些方法可以接受特殊方法等。 我可以在一个类中编写所有方法并为每个方法返回这个,但这不合适!!!

请指导我如何编写高级流畅界面类。

【问题讨论】:

  • 查看 FluentAssertions 的代码库:github.com/dennisdoomen/FluentAssertions 他们可能已经拥有您需要的东西。
  • 您接受的答案仍然可以允许new ConditionCreator() .Add().Or().And().And().And()。这是你想要的还是我误解了你的问题。
  • 有关如何正确解决此问题的更多详细信息,请参阅我的更新答案。

标签: c# fluent-interface method-chaining


【解决方案1】:

为了限制事物,您需要创建并返回一个(可能是多个)“构建器”对象,这些对象可以执行特殊操作,并保留对主类的引用。

public class ConditionCreator 
{
    public ConditionCreator() { ... }

    public SubConditionCreator Add() { ...; return new SubConditionCreator(this); }

    internal ConditionCreator OnAdd() { ...; return this; };
    internal ConditionCreator OnOr() { ...; return this; };
}

public class SubConditionCreator
{
    private ConditionCreator _creator;

    internal SubConditionCreator(ConditionCreator c) { _creator = c; }

    public ConditionCreator And() { return _creator.OnAdd(); }
    public ConditionCreator Or() { return _creator.OnOr(); }
}

使用内部访问来限制使用。

为避免产生垃圾,请在主类中存储一个 SubConditionCreator ref

【讨论】:

    【解决方案2】:

    据我所知,没有真正简单的方法可以解决这个问题。也许 T4 模板可能会有所帮助,但到目前为止,我一直必须构建决策树,在每个节点上都有一个明确的接口。例如;让我们假设您的决策树是一个无限循环,然后(相应地实现):

    interface IStart<T>
    {
       IAndOr Add();
       T End();
    }
    interface IAndOr<T>
    {
       IStart<T> And();
       IStart<T> Or();
    }
    

    如果你想要一个有限循环,那就很难了;说零到两个加:

    interface IStart<T> : IFinish<T>
    {
       IAndOrFirst<T> Add();
    }
    
    interface IAndOrFirst<T>
    {
       ISecond<T> And();
       ISecond<T> Or();
    }
    
    interface ISecond<T> : IFinish<T>
    {
       IAndOrSecond<T> Add();
    }
    
    interface IAndOrSecond <T>
    {
       IFinish<T> And();
       IFinish<T> Or();
    }    
    interface IFinish<T>
    {      
       T End();
    }
    

    您可以(明确地)在一个充当状态机的类中实现这些:

    class ConditionCreator <T> : IStart<T>, IFinish<T>, IAndOrFirst<T>, IAndOrSecond<T> {...}
    

    您将在其中返回 this 以获取 Add() And() Or() 并保持这些状态更改和顺序。

    我希望有人能以更好的方式手动写出每个节点来回答这个问题。

    【讨论】:

      【解决方案3】:

      考虑返回一个只包含And()Or() 的接口。例如:

      public class ConditionCreator : IFluentAndOr
      {
          public IFluentAndOr And() { ... }
          public IFluentAndOr Or() { ... }
      }
      
      public interface IFluentAndOr
      {
          IFluentAndOr And();
          IFluentAndOr Or();
      }
      

      【讨论】:

        【解决方案4】:
                    public class DoEqual
                        {
        
                        }
                        public interface ICanAddWhereValue
                        {
                            ICanAddWhereOrRun IsEqualTo(object value);
                            ICanAddWhereOrRun IsNotEqualTo(object value);
                            IBothEqual BothEqual ( object value );
                        }
        
                        public interface IBothEqual
                        {
                            DoEqual Excute();
                        }
        
        
                        public interface ICanAddWhereOrRun
                        {
                            ICanAddWhereValue Where(string columnName);
                            bool RunNow();
                            DoEqual Excute();
                        }
        
                     public interface ICanAddCondition
                        {
                            ICanAddWhereValue Where(string columnName);
                            bool AllRows();
                        }
        
                namespace BuildAFluentInterface
                {
                    public class WhereCondition
                    {
                        public enum ComparisonMethod
                        {
                            EqualTo,
                            NotEqualTo
                        }
        
                        public string ColumnName { get; private set; }
                        public ComparisonMethod Comparator { get; private set; }
                        public object Value { get; private set; }
        
                        public WhereCondition(string columnName, ComparisonMethod comparator, object value)
                        {
                            ColumnName = columnName;
                            Comparator = comparator;
                            Value = value;
                        }
                    }
                }
        
            using System.Collections.Generic;
        
            namespace BuildAFluentInterface
            {
                public class DeleteQueryWithoutGrammar
                {
                    private readonly string _tableName;
                    private readonly List<WhereCondition> _whereConditions = new List<WhereCondition>();
        
                    private string _currentWhereConditionColumn;
        
                    // Private constructor, to force object instantiation from the fluent method(s)
                    private DeleteQueryWithoutGrammar(string tableName)
                    {
                        _tableName = tableName;
                    }
        
                    #region Initiating Method(s)
        
                    public static DeleteQueryWithoutGrammar DeleteRowsFrom(string tableName)
                    {
                        return new DeleteQueryWithoutGrammar(tableName);
                    }
        
                    #endregion
        
                    #region Chaining Method(s)
        
                    public DeleteQueryWithoutGrammar Where(string columnName)
                    {
                        _currentWhereConditionColumn = columnName;
        
                        return this;
                    }
        
                    public DeleteQueryWithoutGrammar IsEqualTo(object value)
                    {
                        _whereConditions.Add(new WhereCondition(_currentWhereConditionColumn, WhereCondition.ComparisonMethod.EqualTo, value));
        
                        return this;
                    }
        
                    public DeleteQueryWithoutGrammar IsNotEqualTo(object value)
                    {
                        _whereConditions.Add(new WhereCondition(_currentWhereConditionColumn, WhereCondition.ComparisonMethod.NotEqualTo, value));
        
                        return this;
                    }
        
                    #endregion
        
                    #region Executing Method(s)
        
                    public void AllRows()
                    {
                        ExecuteThisQuery();
                    }
        
                    public void RunNow()
                    {
                        ExecuteThisQuery();
                    }
        
                    #endregion
        
                    private void ExecuteThisQuery()
                    {
                        // Code to build and execute the delete query
                    }
                }
            }
        <br>
        In Main Test with 
        public class myclass
        {
        private static void Main(string[] args)
                {
        DoEqual x3 =
                        DeleteQueryWithGrammar.DeleteRowsFrom("Account")
                            .Where("Admin")
                            .IsNotEqualTo("Admin")
                            .Where("Admin")
                            .BothEqual("X")
                            .Excute();
        }
        }
        

        【讨论】:

          【解决方案5】:

          这似乎行得通。

            public class ConditionCreator
            {
               private Decision decision;
          
               public ConditionCreator() { decision = new Decision(this); }
               public Decision Add() { return decision; }
          
               public class Decision
               {
                  private ConditionCreator creator;
          
                  public Decision(ConditionCreator creator) { this.creator = creator; }
                  public ConditionCreator And() { return creator; }
                  public ConditionCreator Or() { return creator; }
                  public Condition Create() { return new Condition(); }
               }
            }
          

          现在,当您拨打电话时,您只能使用这样的模式:

               var condition = new ConditionCreator()
                   .Add()
                   .Or()
                   .Add()
                   .And()
                   .Add()
                   .Create();
          

          【讨论】:

            猜你喜欢
            • 2012-07-18
            • 1970-01-01
            • 2016-02-07
            • 1970-01-01
            • 2011-07-10
            • 2012-05-01
            • 1970-01-01
            • 2011-02-23
            • 1970-01-01
            相关资源
            最近更新 更多