c#将方法限制为其他方法

时间:2016-12-29 11:35:06

标签: c# chaining method-chaining

我有一个包含几种方法的类,例如:

class mySqlTool{

    private string _values, _table, _condition, _result;

    public mySqlTool Select(string values = null){
        //this is REQUIRED
        _values = string.Format("select {0} ", values);
        return this;
    }

    public mySqlTool Update(string table){
        //this is REQUIRED
        _table = table;
        return this;
    }

    public mySqlTool Set(string name, String value){
        //this is REQUIRED
        //handle name and value
        return this;
    }

    public mySqlTool From(string table = null){
        //this is REQUIRED
        _table = table;
        return this;
    }
    public mySqlTool Where(string condition = null){
        //this is OPTIONAL
        _condition = condition;
        return this;
    }
    public string Execute(){
        //this is REQUIRED
        //this is samplecode, of course here is checked if its select or update
        //but to keep it short i erased it

        statement = string.Format("{0} {1}", _values, _table);

        if (!string.IsNullOrEmpty(_condition))
        {
            statement += string.Format(" where {0}", _condition);
        }
        //do some with statemen and fill result
        return _result;
    }
}

现在我以这种链式方式使用它:

MySqlTool t = new MySqlTool();
string result = t.Select("a,b,c").From("x").Where("foo=bar").Execute();

当我点击DOT (.)时,我的VS为我提供了可用的方法。

我的问题是,我想在使用其他方法之前拒绝使用某些方法,例如:

MySqlTool.Where().Select().From().Execute();

在这种情况下,.C()不应该被调用,因为.A()被调用了。所以澄清什么是允许的,什么不是,这里有一个小清单

//Allowed
t.Select().From().Execute();
t.Select().From().Where().Execute();
t.Update().Set().Set().Set().Where().Where().Where().Execute();

//not Allowed
t.Select().Where().Execute();
t.Select().Select().Select().From().Execute();
t.From()...
t.Where()...
t.Execute()....

我读了一些有关接口和状态的内容,但我不确定这是否是我正在搜索的内容。

所以我的问题:

这是我想要的吗?

如果是,该技术如何调用?

1 个答案:

答案 0 :(得分:11)

一般说明 - 请参阅结束以获取具体评论

  

这是我想要的吗?

不在同一个班级内,没有。编译器如何知道你已经调用了什么? (想象一下,你有一个类型为Test的参数的方法 - 应该有哪些方法可以调用?)类型系统决定什么是有效的,什么不是 - 所以如果有不同的有效操作集,那表明不同类型。

所能做的是具有代表不同状态的不同类型,这些类型仅包含状态转换的适当方法。所以你可以这样:

class Test0 // Initial state
{
    public Test1 A() { ... }
}

class Test1 // After calling A
{
    public Test2 B() { ... }
}

class Test2 // After calling B
{
    // This returns the same type, so you can call B multiple times
    public Test2 B() { ... }

    // This returns the same type, so you can call C multiple times
    public Test2 C() { ... }

    public string DoSomething() { ... }
}

然后你可以使用:

Test0 t = new Test0();
string x1 = t.A().B().DoSome();
string x2 = t.A().B().C().DoSome();
string x3 = t.A().B().B().B().C().C().C().DoSome();

...但是您的无效案例无法编译。

它有效,但它非常难看。在不知道方法要实现的目的的情况下,很难提出其他任何建议 - 但在许多情况下,使用可选参数的单个方法可能更好,或者可能是构建器模式。

另一种方法是使用单个类,而不是在编译时在执行时验证调用。这在编码时不太有用,但避免了大量的类型。

另一个替代方案是拥有一个类 - 并创建一个实例 - 但使用接口来表示状态。您的类将实现所有接口,因此它仍然可以返回this

interface IStart
{
    IMiddle A();
}

interface IMiddle
{
    IFinal B();
}

interface IFinal
{
    IFinal B();
    IFinal C();
    string DoSomething();
}

class Test : IStart, IMiddle, IFinal
{
    public IMiddle A(string x = null) { return this; }
    public IFinal B(string x = null) { return this; }
    public IFinal C(string x = null) { return this; }
    public string DoSomethign { ... }
}

然后你就有了:

IStart t = new Test();
string x1 = t.A().B().DoSome();
string x2 = t.A().B().C().DoSome();
string x3 = t.A().B().B().B().C().C().C().DoSome();

但这对我来说感觉很不对劲。我希望ABC方法能够以某种方式有效地改变状态 - 因此具有单独的类型将指示哪个状态可用。在第一个示例中,Test0肯定具有A调用提供的状态,但Test1 确实 ... Test2实例的状态由AB提供,可能还有C

具体示例

对于给出的具体示例,我可能只是让构造函数处理 required 信息(表名)并使用属性/索引器来完成剩下的工作。我可能会从更新中分离出一个查询命令:

SqlQuery query = new SqlQuery("table")
{
    Columns = { "a", "b", "c" },
    Where = { "foo=bar" } // Not sure how you're parameterizing these
};

SqlUpdate update = new SqlUpdate("table")
{
    // Which columns to update with which values
    ["a"] = 10,
    ["b"] = 20,
    Where = { "foo=bar" } // Not sure how you're parameterizing these
};

在每种情况下都会有Execute方法返回相应的结果。