【发布时间】:2016-03-09 15:41:36
【问题描述】:
我正在尝试实现一个复杂的构建器来帮助我的测试环境。为此,我重构了我的代码以拥有一个方法:
public TestContext Add<T>(Action<IBuilder<T>> configurator) where T : class, new()
{
IBuilder<T> builder = CreateBuilderOf<T>();
configurator(builder);
T item = builder.GetItem();
RepositoryOf<T>().Insert(item);
SetCurrent(item);
return this;
}
当我需要在调用方法时指定配置时出现问题:
TestContext.Instance.Add<Person>(personBuilder => ((PersonBuilder)personBuilder).Name("SMITH"));
我需要能够使用配置器中的类型特定方法,这些方法由具体的构建器实现,例如:
public PersonBuilder : IBuilder<Person>
{
private Person Item;
public PersonBuilder() { Item = new Person(); }
public Name(string mame) { Item.Name = name; }
public Person GetItem() { return Item; }
}
显然,不允许将Action<PersonBuilder> 作为Action<IBuilder<Person>> 传递,即使PersonBuilder 实现了IBuilder<Person>,因此是演员表。
我非常愿意:
- 不需要在 lambda 中进行转换,而是在它的开头,例如
(PersonBuilder personBuilder) => personBuilder.Name("SMITH"),但这归结为Action<PersonBuilder>的一个实例,因此同样无效; - 在 Add 的参数中使用诸如
BuildSimplePerson(PersonBuilder builder)之类的函数:Add<Person>(BuildSimplePerson)
我想我可以通过两个BuildSimplePersons 实现进行类型转换,例如:
private void BuildSimplePerson(IBuilder<Person> builder)
{
BuildSimplePerson(builder as PersonBuilder);
}
private void BuildSimplePerson(PersonBuilder builder)
{
builder.Name("SMITH");
}
但这并不是一个真正优雅的解决方案。
我还意识到将Action<PersonBuilder> 作为Action<IBuilder<Person>> 传递是不正确的,因为我们不知道该函数的参数是否真的是PersonBuilder 或IBuilder<Person> 的任何其他实现。
我怎样才能做得更好?
【问题讨论】:
-
您的问题源于一个简单的事实,即您将
IBuilder<Person>和PersonBuilder视为相同,而实际上它们并非如此。您的问题假定CreateBuilderOf<T>返回一个PersonBuilder但它可以返回任何实现IBuilder<Person>在这种情况下您的演员无论如何都会失败。 -
@DanielHilgarth 是的,我意识到这一点。我重构了我的问题及其标题,以显示我想在现实中了解的内容:我怎样才能保留通用的 Add
方法并且仍然有一种特定于类型的配置的方法? -
@Eregrith:您是在问是否可以为
Person提供一个特殊的Add方法? -
@DanielHilgarth 我认为不是。很抱歉,如果我不太清楚,这有点难以解释。我想只保留一个如上所述的通用 Add
方法,它将接收作为参数的 lambda(或一般的操作)来配置此类 T 项。问题是可用的配置内容和方法应该取决于T(人名,合同号...)
标签: c# templates lambda delegates