【问题标题】:c# restrict methods to other methodsc#将方法限制为其他方法
【发布时间】:2016-12-29 11:35:05
【问题描述】:

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

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()....

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

所以我的问题:

这是我想要的吗?

如果是,这个技术是怎么命名的?

【问题讨论】:

  • .net 的状态机?
  • 这种技术被称为“流利的接口”。
  • 不是在编译时,在运行时你可能会携带一个状态。只是...不要。
  • 检查这个类似的问题:stackoverflow.com/questions/41319485/…

标签: c# chaining method-chaining


【解决方案1】:

一般描述 - 见末尾特定的 cmets

这是我想要的吗?

不在同一个班级,不。编译器怎么知道你已经调用了什么? (想象一下,你有一个带有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 确实...和@ 987654333@ 实例的状态由AB 提供,可能还有C

具体例子

对于给出的具体示例,我可能只是让构造函数处理必需的信息(表名),其余部分使用属性/索引器。我可能会将查询命令与更新分开:

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 方法返回相应的结果。

【讨论】:

  • 这还能链接吗?
  • @Dwza:你试过了吗?为什么它不能被链接?
  • 我刚刚阅读并询问...以这种方式使用它需要一些时间来重做我的代码^^我只是提供了一个示例。我的来源更大。我将构建一个示例程序来测试它。我会回来的:D
  • @Dwza:请体谅他人的时间。当您清楚地可以自己检查某些东西时,就值得这样做。你要求人们帮助你:你越能表明你愿意尽你所能,他们就越愿意帮助你不能做的事情.
  • 在将 Test1 转换为 Test0 时会出错。而且,如果我要发布我的整个源代码或相关部分,则需要进行大量解释,因此最终会出现在没有答案的封闭帖子中。我这么说是因为我不止一次遇到过这种情况。
猜你喜欢
  • 1970-01-01
  • 2011-06-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-13
相关资源
最近更新 更多