C#中的流畅接口

8

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

我们有一些对象作为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选项。我不确定是否想要删除.With部分,因为这将公开对象的所有方法,而不仅仅是流畅的方法。
建议和一些好的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对象被构造并设置正确,但它与预期的用法不兼容。

2个回答

8
SqlParameterOption的所有方法都可以作为实例方法返回相同的对象:
class SqlParameterOption
 {
    public SqlParameterOption Precision(int p) {/* ... */; return this;}
    public SqlParameterOption Substitute() {/* ... */; return this;}
    /* ... */       
 }

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

关于先建立状态以便稍后应用和直接在每次调用时应用的问题,如果两种方式都没有真正不可逆的副作用,那么选择哪种方式就取决于你的个人喜好。如果每次方法调用都会提交选项,并且有可能需要撤销这些选项,则建议先构建状态,然后再应用它们。如果参数对象在应用属性时为您执行属性之间的验证,则最好选择直接应用,这样您可以立即获得验证反馈。


1

你可以有重载的方法。例如,如果它是Substitute()。通常情况下,您不能同时拥有静态版本和实例版本的方法,但扩展方法可能会有所用处……但如果这两个Substitute版本具有不同的含义,最好还是返回不同类型,以便两个Substitute()的变体不会发生冲突。


在这种情况下,关于含义不会有任何冲突,问题更多地是关于我如何组织代码以达到我想要的语法方面。实例化选项对象可以解决这个问题,就像 marxidad 展示的那样。 - Lasse V. Karlsen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接