C# 具有继承的专业化和专业化方法返回的Fluent builder(奇怪的重复模板)

C# 具有继承的专业化和专业化方法返回的Fluent builder(奇怪的重复模板),c#,generics,fluent,C#,Generics,Fluent,我正在尝试编写一个其他开发人员可以流利使用的数据管道生成器。如果我从一个预期结果的示例开始,这可能是最简单的: var b=newbuilder(); b、 批处理().BatchedOperation().Unbatch().UnbatchedOperation()等。 我已经能够创建一个抽象构建器,它可以由派生构建器使用奇怪的重复模板模式进行专门化 我正在努力解决的一个关键问题是,某些操作应该只有在某些其他操作之后才能进行。具体来说,管道中有两种主要类型的操作:对实例集起作用的操作和对单个

我正在尝试编写一个其他开发人员可以流利使用的数据管道生成器。如果我从一个预期结果的示例开始,这可能是最简单的:

var b=newbuilder();
b、 批处理().BatchedOperation().Unbatch().UnbatchedOperation()等。
我已经能够创建一个抽象构建器,它可以由派生构建器使用奇怪的重复模板模式进行专门化

我正在努力解决的一个关键问题是,某些操作应该只有在某些其他操作之后才能进行。具体来说,管道中有两种主要类型的操作:对实例集起作用的操作和对单个实例起作用的操作。因此,仅在已批处理的管道上执行
批处理操作
,在已取消批处理的管道上执行
取消批处理操作
,才有效

为了下面的示例代码,我将管道视为在任何一个时间处于两种形式之一:
Foo
形式或
Bar
形式。这基本上等同于批处理或取消批处理,但它将代码缩减到核心问题,而不必考虑批处理或取消批处理的确切含义,并消除了混乱

首先,假设我从以下内容开始,即基本CRTP:

public abstract class Builder<TBuilder> where TBuilder : Builder<TBuilder>
{
    protected TBuilder builder;
    internal Builder() => builder = (TBuilder)this;
    public TBuilder Bar() => builder;
    public TBuilder Foo() => builder;
}
这是不好的,因为一旦管道被
Foo()
d,就不可能再
Foo()
它,因为它现在处于
Bar()
able状态。很明显,这会抛出一个运行时错误,但它不会在编译时被捕获(特别是intellisense)

我可以使用接口限制管道操作的结果:

公共接口IBar{IFoo Bar();}
公共接口IFoo{IBar Foo();}
公共抽象类生成器:IFoo、IBar
其中TBuilder:Builder
{
受保护的TBuilder-builder;
内部生成器()=>Builder=(TBuilder)此;
public IFoo Bar()=>builder;
公共IBar Foo()=>builder;
}
然而,这会使它回到一个不可继承的构建器,因为现在我的派生构建器一旦被
Foo()
d使用就不能工作了。在这一点上,它不再是
Special()
d,因为它现在是
IBar
,而不是
SpecialBuilder

似乎我需要额外的专用接口:

公共接口ISpecialFoo:IFoo{T Special();}
公共接口ISpecialBar:IBar{T Special();}
当然,抽象的
Builder
仍然不能指定
Bar()
IFoo
中返回,因为它仍然不是
SpecialBuilder
,因此不能是
Special()
d。因此,似乎接口返回类型本身也需要遵循奇怪的循环模板模式

这就是我大脑开始受伤的地方。我想也许是这样:

公共抽象类生成器
:IFoo,IBar//这两个错误
其中TBuilder:Builder、IFoo、IBar、TFoo、TBar
其中TFoo:IFoo
哪里有TBar:IBar
{
受保护的TBuilder-builder;
内部生成器()=>Builder=(TBuilder)此;
公共TFoo Bar()=>builder;
公共TBar Foo()=>builder;
}
但这给了我两个对称的错误,即试图从上述接口继承:

“生成器”未实现接口成员 IFoo.Foo()Builder.Foo()无法 实现“IFoo.Foo()”,因为它没有 “IBar”的匹配返回类型

“生成器”未实现接口成员 “IBar.Bar()。”Builder.Bar()无法 实现“IBar.Bar()”,因为它没有 匹配“IFoo”的返回类型

这可能吗?我真的很想给这段代码的用户提供编译时帮助,例如,intellisense只显示生成器状态的有效操作。但他们的收获显然是我在这里的痛苦

下面是一个完整的控制台应用程序,它展示了我所获得的最接近的结果:

公共接口ISpecial{SpecialBuilder Special();}
公共接口ISpecialFoo:IFoo,ISpecial{}
公共接口ISpecialBar:IBar,ISpecial{}
公共接口IBar{IFoo Bar();}
公共接口IFoo{IBar Foo();}
内部课程计划
{
私有静态void Main(字符串[]args)
{
var b=新的SpecialBuilder();
b、 特殊().Foo().Special().Bar();
}
}
公共抽象类生成器
其中TBuilder:Builder、TFoo、TBar
其中TFoo:IFoo
哪里有TBar:IBar
{
受保护的TBuilder-builder;
内部生成器()=>Builder=(TBuilder)此;
公共TFoo Bar()=>builder;
公共TBar Foo()=>builder;
}
公共类特殊生成器:生成器、ISPSpecialFoo、ISPSpecialBar
{
公共SpecialBuilder():base(){}
public SpecialBuilder Special()=>builder;
}

只需将类型信息挂起即可

//我将通用接口移到了CRTP基内部
//为CRTP使用者保存一些输入。
//您还可以将IState1和IState2移到外部
//它们将需要通用参数TIState1、TIState2。
公共抽象类生成器
:Builder.IState1,
Builder.IState2
//这个约束会编译(或不会编译)。
其中TBuilder:Builder、TIState1、TIState2
其中TIState1:Builder.IState1
其中TIState2:Builder.IState2
{
公共接口IState1{TIState2 OpState1();}
公共接口IState2{TIState1 OpState2();}
私人建筑商;
受保护的生成器(){that=(TBuilder)this;}
public TIState2 OpState1(){返回该;}
public TIState1 OpState2(){返回该;}
}
公共密封类MyBuilder
:建筑商,
MyBuilder.IMyState1、MyBuilder.IMyState2
{
公共接口IMySpecial{MyBuilder OpSpecial();}
公共接口IMyState1:IStat
var b = new SpecialBuilder();
b.Foo().Foo().etc