C#中的Fluent接口

本文关键字:接口 Fluent 中的 | 更新日期: 2023-09-27 17:47:50

我有一个关于流畅接口的问题。

我们有一些对象用作SQL接口的参数对象,下面是一个例子:

using (DatabaseCommand cmd = conn.CreateCommand(
    "SELECT A, B, C FROM tablename WHERE ID = :ID",
    SqlParameter.Int32(":ID", 1234)))
{
    ...
}

对于其中的一些参数,我想启用一些专门的选项,但我不想在Int32方法中添加更多的属性(这只是众多方法中的一个),而是想研究流畅的接口。

这里有一个例子,我添加了我正在研究的内容:

SqlParameter.Int32(":ID", 1234).With(SqlParameterOption
    .Substitute
    .Precision(15)
)

我知道这两个选项对这种类型的参数没有意义,但这不是问题所在。

在上面的情况下,Substitute必须是SqlParameterOption类的静态属性(或者方法,如果我只是添加一些括号的话),而Precision必须是实例方法。

如果我重新订购呢?

SqlParameter.Int32(":ID", 1234).With(SqlParameterOption
    .Precision(15)
    .Substitute
)

那么Substitute必须是实例属性,Precision必须是静态方法。当然,这不会编译,我不能同时具有相同名称的静态和非静态属性或方法。

我该怎么做?我是不是完全走错了路?

在重读这个问题时,我有一个想法,下面这种不同的语法会更有意义吗?

SqlParameter.Int32(":ID", 1234).With
    .Precision(15)
    .Substitute

在这种情况下,这两个方法都是With返回的实例方法,这将是SqlParameter选项的专用类或接口。我不确定我是否要转储。对于部分,因为这将暴露对象的所有方法,而不仅仅是流畅的方法。

建议和一些好的url将是最受欢迎的,我已经搜索了很多例子,但他们倾向于显示这样的例子:

order
    .AddFreeShipping()
    .IncludeItem(15)
        .SuppressTax();

(从本页起)


编辑:回复后跟进发件人@marxidad:

class SqlParameterOption
{
    public SqlParameterOption Precision(int p) {/* ... */; return this;}
    public SqlParameterOption Substitute() {/* ... */; return this;}
    /* ... */       
}
/* ... */
SqlParameter.Int32(":ID", 1234).With(new SqlParameterOption()
                                           .Precision(15)
                                           .Substitute());

使用这种方法,With必须获取对象,并将其应用于参数。我对此很满意。

如果我使用我添加的语法作为例子,它会是这样的:

SqlParameter.Int32(":ID", 1234).With
                               .Precision(15)
                               .Substitute());

在这种情况下,With不知道链何时结束,因此每个选项都必须直接应用其效果。

首选项是什么?选项建立了一个稍后必须应用的效果对象,还是每个效果都直接应用其效果?

我的决定:正如@marxidad所说,如果这些变化是不可逆转的,并且可能会发生逆转,那么我会选择建立状态并在某个时候失败(只有一个例外)。

但是,在这种情况下,我将使用一种更简单的方法,直接修改SqlParameter对象。

在这种情况下,我的代码将如下所示:

SqlParameter.Int32(":ID", 1234).With
                               .Precision(15)
                               .Substitute());

编辑:啊,当我只关注一件事时,情况就是这样。

我不能使用这种语法,我将按照@marxidad:的建议使用以下语法

SqlParameter.Int32(":ID", 1234).With(new SqlParameterOption()
                                           .Precision(15)
                                           .Substitute());

当然,原因是将SqlParameter对象作为参数的方法无法处理with返回的对象,因此,尽管SqlParameter的对象构造和设置正确,但它与预期用途不兼容。

C#中的Fluent接口

SqlParameterOption's方法都可以是返回相同对象的实例方法:

class SqlParameterOption
 {
    public SqlParameterOption Precision(int p) {/* ... */; return this;}
    public SqlParameterOption Substitute() {/* ... */; return this;}
    /* ... */       
 }
/* ... */
SqlParameter.Int32(":ID", 1234).With(new SqlParameterOption()
                                           .Precision(15)
                                           .Substitute());

回复:建立稍后应用的状态,而不是每次调用都直接应用,如果在这两种情况下都没有真正的无法验证的副作用,那么这无关紧要,取决于您的个人品味。如果每个方法调用都提交了选项,并且您可能希望撤消该选项,那么您可能希望先建立状态,然后应用它。如果参数对象在应用属性时为您在属性之间进行验证,那么最好使用直接应用程序,这样您就可以正确地获得验证反馈。

您可以重载方法。例如,如果它是Substitute()。通常不能同时拥有方法的静态版本和实例版本,但扩展方法可能会有一些用处。。。但是,如果Substitute的两个版本有不同的含义,那么简单地返回不同的类型会更干净,这样Substitute()的两个变体就不会冲突。