c# restrict methods to other methods

1.1k views Asked by At

I have a class that contains several methods eg.:

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

now I use this in this chaining way:

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

My VS provides me with available methods when I hit DOT (.).

My problem is, I want to denie to use some methods before other methods have been used eg:

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

In this case .C() should not be callable befor .A() was called. So to clarify whats allowed and whats not, here a small list

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

I read some about interfaces and also about state but I'm not sure if this is what im searching for.

So my question:

Is this what I want even possible?

If yes, how is this technique called?

1

There are 1 answers

15
Jon Skeet On BEST ANSWER

General description - see end for specific comments

Is this what I want even possible?

Not within the same class, no. How would the compiler know what you'd already called? (Imagine you had a method with a parameter of type Test - what methods should be available to call on that?) The type system decides what's valid and what's not - so if there are different sets of valid operations, that suggests different types.

What you can do is have different types representing the different states, which will only include the appropriate methods for state transitions. So you could have something like this:

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

Then you can use:

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

... but your invalid cases wouldn't compile.

It works, but it's pretty ugly. Without knowing what the methods are meant to achieve, it's hard to suggest anything else - but in many cases a single method with optional parameters may be better, or possibly a builder pattern.

An alternative is to use a single class and validate the calls at execution time instead, of at compile time. That's less helpful when coding, but avoids having a huge mess of types.

Yet another alternative would be to have a single class - and create a single instance - but use interfaces to represent the state. Your class would implement all the interfaces, so it could still just return 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 { ... }
}

Then you'd have:

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

But this feels pretty wrong to me. I'd expect the A, B and C methods to be effectively changing state in some way - so having separate types would indicate which state is available. In the first example, a Test0 definitely doesn't have the state provided by the A call, but a Test1 does... and a Test2 instance has state provided by A and B, and possibly C.

Specific example

For the specific example given, I'd probably just make the constructor handle the required information (the table name) and use properties/indexers for the rest. I'd probably separate out a query command from updates:

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

And:

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

In each case there'd be an Execute method returning the appropriate results.