1

I have a class that defines a fluent interface, including an Else method, which can only be called once. The problem I therefore have, is how to tell if it has been called. Currently, I'm doing this via an extra bool field:

public class FluentMatcher<T1, T2, TResult>
{
    private bool _elseValueSet;
    private TResult _elseValue;

    ...

    public FluentMatcher<T1, T2, TResult> Else(TResult resultIfElseRequired)
    {
        if (_elseValueCalled)
        {
            throw new ElseAlreadyAddedException();
        }
        _elseValue = resultIfElseRequired;
        _elseValueSet = true;
        return this;
    }
}

I am uncomfortable with this though and so I'm considering something like:

public class FluentMatcher<T1, T2, TResult>
{
    private Initialisable<TResult> _elseValue;

    ...

    public FluentMatcher<T1, T2, TResult> Else(TResult resultIfElseRequired)
    {
        if (_elseValue != null)
        {
            throw new ElseAlreadyAddedException();
        }
        _elseValue = new Initialisable<TResult>(resultIfElseRequired);
        return this;
    }
}

This raises two questions:

  1. Is there already a way to test if the field has been initialised? I'm assuming not, as it could be a struct and thus cannot be null. Did I miss something though?
  2. I have a feeling that I'm not really fixing anything with my new version and that I'm playing with semantics. The first version works just fine, I just think it ugly. Is there a good reason to not use a bool in this case?
4

1 回答 1

2

你的Else方法应该返回另一个非常相似的接口——一个没有方法的接口Else,这样你就不能调用它两次。

这是我正在开发的应用程序的示例。

public class AddColumn
{
    internal AddColumn()
    {

    } 

    public NamedAddColumn Named(string name)
    {
        return new NamedAddColumn(name);
    }
}

public class NamedAddColumn
{
    protected string Name {get; set;}

    internal NamedAddColumn(string name)
    {
        Name = name;
    }

    public VarcharTypedAddColumn Varchar
    {
        get
        {
            return new VarcharTypedAddColumn(Name);
        }
    }
}

这是一个用于创建数据库迁移的流畅界面。如您所见,该Named方法返回一个不同的类,具有非常相似的语义含义(它们都代表一个列)——但是,这个类不允许您两次“命名”该列。

这是您使用此界面的方式:

        db.AlterTable(tb => tb
                                .Named("Users")
                                .AddColumn(column => column
                                                         .Named("age")
                                                         .Integer
                                )
                                .AddColumn(column => column
                                                         .Named("username")
                                                         .Varchar
                                )

编辑 在你的情况下,你会有这样的事情:

public class FluentMatcher<T1, T2, TResult>
{
    /*
       Methods A B C

     */

    public FluentMatcherWithElse<T1, T2, TResult> Else(TResult resultIfElseRequired)
    {
        return new FluentMatcherWithElse(resultIfElseRequired);
    }
}

public class FluentMatcherWithElse<T1, T2, TResult>
{
    internal FluentMatcherWithElse(TResult resultIfElseRequired) { ... }

    /*
       Methods A B C - but NO else method

     */
}
于 2013-11-06T14:53:28.793 回答