cfe*_*uke 16 c# generics builder
我正在创建一系列构建器来清理语法,为我的模拟创建域类,作为改进整体单元测试的一部分.我的构建器基本上填充了一个域类(例如a Schedule),其中一些值通过调用相应的值WithXXX并将它们链接在一起来确定.
我在构建器中遇到了一些共性,我想将它抽象到基类中以增加代码重用.不幸的是,我最终看起来像:
public abstract class BaseBuilder<T,BLDR> where BLDR : BaseBuilder<T,BLDR>
where T : new()
{
public abstract T Build();
protected int Id { get; private set; }
protected abstract BLDR This { get; }
public BLDR WithId(int id)
{
Id = id;
return This;
}
}
Run Code Online (Sandbox Code Playgroud)
特别注意protected abstract BLDR This { get; }.
域类构建器的示例实现是:
public class ScheduleIntervalBuilder :
BaseBuilder<ScheduleInterval,ScheduleIntervalBuilder>
{
private int _scheduleId;
// ...
// UG! here's the problem:
protected override ScheduleIntervalBuilder This
{
get { return this; }
}
public override ScheduleInterval Build()
{
return new ScheduleInterval
{
Id = base.Id,
ScheduleId = _scheduleId
// ...
};
}
public ScheduleIntervalBuilder WithScheduleId(int scheduleId)
{
_scheduleId = scheduleId;
return this;
}
// ...
}
Run Code Online (Sandbox Code Playgroud)
因为BLDR不是BaseBuilder类型,所以我不能return this在WithId(int)方法中使用BaseBuilder.
在这里公开带有属性的子类型是abstract BLDR This { get; }我唯一的选择,还是我错过了一些语法技巧?
更新(因为我可以更清楚地说明为什么我这样做了):
最终结果是让构建器构建配置文件的域类,人们希望以[程序员]可读格式从数据库中检索这些类.......没有错
mock.Expect(m => m.Select(It.IsAny<int>())).Returns(
new Schedule
{
ScheduleId = 1
// ...
}
);
Run Code Online (Sandbox Code Playgroud)
因为那已经很可读了.替代构建器语法是:
mock.Expect(m => m.Select(It.IsAny<int>())).Returns(
new ScheduleBuilder()
.WithId(1)
// ...
.Build()
);
Run Code Online (Sandbox Code Playgroud)
我正在寻找使用构建器(以及实现所有这些WithXXX方法)的优势是抽象出复杂的属性创建(自动扩展我们的数据库查找值,而Lookup.KnownValues不是明显地访问数据库)并让构建器提供通常可重用的测试配置文件对于域类...
mock.Expect(m => m.Select(It.IsAny<int>())).Returns(
new ScheduleBuilder()
.AsOneDay()
.Build()
);
Run Code Online (Sandbox Code Playgroud)
Jon*_*eet 11
我可以说的是,如果是这样做的一种方式,我想知道它太-我用的正是我的这种模式Protocol Buffers的端口.事实上,我很高兴看到其他人已经采取了它 - 这意味着我们至少有可能是正确的!
我知道这是一个老问题,但我认为你可以使用简单的转换来避免abstract BLDR This { get; }
结果代码将是:
public abstract class BaseBuilder<T, BLDR> where BLDR : BaseBuilder<T, BLDR>
where T : new()
{
public abstract T Build();
protected int Id { get; private set; }
public BLDR WithId(int id)
{
_id = id;
return (BLDR)this;
}
}
public class ScheduleIntervalBuilder :
BaseBuilder<ScheduleInterval,ScheduleIntervalBuilder>
{
private int _scheduleId;
// ...
public override ScheduleInterval Build()
{
return new ScheduleInterval
{
Id = base.Id,
ScheduleId = _scheduleId
// ...
};
}
public ScheduleIntervalBuilder WithScheduleId(int scheduleId)
{
_scheduleId = scheduleId;
return this;
}
// ...
}
Run Code Online (Sandbox Code Playgroud)
当然,您可以将构建器封装为
protected BLDR This
{
get
{
return (BLDR)this;
}
}
Run Code Online (Sandbox Code Playgroud)