Search code examples
c#genericsbuildercreation-pattern

Generic abstract builder properties needs casting


I've build a generic (abstract) builder which will provide basic implementation for entity builders that will be used during testing.

This is the entity base class:

public abstract class Entity : IObjectState
{
    [NotMapped]
    public ObjectState ObjectState { get; set; }
}

This is the IKey interface:

public interface IKey
{
    int Id { get; set; }
}

This is the Builder class:

public abstract class Builder<T> where T : Entity, IKey, new()
{
    protected int _id { get; set; }
    protected ObjectState _objectState { get; set; }

    public Builder()
    {
        _objectState = ObjectState.Added;
    }

    public virtual Builder<T> WithId(int id)
    {
        this._id = id;
        return this;
    }

    public virtual Builder<T> HavingObjectState(ObjectState objectState)
    {
        _objectState = objectState;
        return this;
    }

    public static implicit operator T(Builder<T> builder)
    {
        return new T
        {
            Id = builder._id,
            ObjectState = builder._objectState
        };
    }
}

This is a sample UnitBuilder implementation:

public class UnitBuilder : Builder<Unit>
{
    private string _shortDescription;
    private string _longDescription;

    public UnitBuilder WithShort(string shortDescription)
    {
        _shortDescription = shortDescription;
        return this;
    }

    public UnitBuilder WithLong(string longDescription)
    {
        _longDescription = longDescription;
        return this;
    }

    public static implicit operator Unit(UnitBuilder builder)
    {
        return new Unit
        {
            Id = builder._id,
            ObjectState = builder._objectState,
            Short = builder._shortDescription,
            Long = builder._longDescription
        };
    }
}

And this is the problem I'm having:

sample

The error:

Error CS1061 'Builder' does not contain a definition for 'WithShort' and no extension method 'WithShort' accepting a first argument of type 'Builder' could be found (are you missing a using directive or an assembly reference?)



I understand what is going on but I would like a better (more elegant) solution than thirdUnit.

UPDATE:

As per suggestion I added the following to the UnitBuilder class:

public new UnitBuilder WithId(int id)
{
    return (UnitBuilder)base.WithId(id);
}

public new UnitBuilder WithObjectState(ObjectState objectState)
{
    return (UnitBuilder)base.WithObjectState(objectState);
}

But now I don't see any point in the base class... This has to be a general generic base class problem, how do other people handle this? Maybe the thirdUnit solution IS elegant but I'm just being difficult about it? :)


Solution

  • The answer is simple, your base class builder methods have to be called last and cannot be chained with your more specific builder classes as it returns the generic. So simply change your code to:

    Unit secondUnit = new UnitBuilder()
        .WithShort("ShortDesc")
        .WithId(10);
    

    That's it!