How to use generics to handle type-specific configurations in an Action<IBuilder<T>>?

Viewed 48

I am trying to implement a complex builder to help my testing context. To do that I refactored my code to have a method :

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;
}

The problem arises when I need to specify the configuration when calling the method :

TestContext.Instance.Add<Person>(personBuilder => ((PersonBuilder)personBuilder).Name("SMITH"));

I need to be able to use type specific methods in the configurator, which are implemented by the concrete builders, such as :

public PersonBuilder : IBuilder<Person>
{
  private Person Item;

  public PersonBuilder() { Item = new Person(); }

  public Name(string mame) { Item.Name = name; }

  public Person GetItem() { return Item; }
}

Apparently, passing an Action<PersonBuilder> as an Action<IBuilder<Person>> is not allowed, even though PersonBuilder implements IBuilder<Person>, hence the cast.

I would very much like to either :

  • Need not to cast inside the lambda but rather at the start of it, e.g.
    (PersonBuilder personBuilder) => personBuilder.Name("SMITH"), but that boils down to being an instance of Action<PersonBuilder> and therefore is equally invalid;
  • Use a function such as BuildSimplePerson(PersonBuilder builder) in Add's argument : Add<Person>(BuildSimplePerson)

I think I can do a type casting through two BuildSimplePersons implementations such as :

private void BuildSimplePerson(IBuilder<Person> builder)
{
  BuildSimplePerson(builder as PersonBuilder);
}
private void BuildSimplePerson(PersonBuilder builder)
{
  builder.Name("SMITH");
}

But that is not really an elegant solution.

I also realize passing Action<PersonBuilder> as an Action<IBuilder<Person>> is incorrect because we don't know if the argument to that function will truly be a PersonBuilder or any other implementation of IBuilder<Person>.

How can I do better ?

1 Answers
Related