2016-05-29 13 views
1

Ich habe eine generische (abstrakte) Builder erstellen, die grundlegende Implementierung für Entity-Builder, die während des Testens verwendet werden wird.Generische abstrakte Builder-Eigenschaften benötigt Casting

Dies ist die Einheit Basisklasse:

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

Dies ist der IKey Schnittstelle:

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

Dies ist die Builder Klasse:

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

ist ein Beispiel UnitBuilder Umsetzung:

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

Und das ist das Problem, das ich habe:

sample

Der Fehler:

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?)



Ich verstehe was Es geht weiter, aber ich hätte gerne eine bessere (elegantere) Lösung als thirdUnit.

UPDATE:

Gemäß Vorschlag, den ich im folgenden auf die UnitBuilder Klasse hinzugefügt:

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

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

Aber jetzt ich sehe keinen Punkt in der Basisklasse ... Das muss ein allgemeines generisches Basisklassenproblem sein, wie gehen andere Leute damit um? Vielleicht ist die thirdUnit Lösung elegant, aber ich bin nur schwierig dabei? :)

+1

Sie könnten eine neue withId Methode mit dem Schlüsselwort deklarieren ‚neuen‘ in der UnitBuilder Klasse, die die Methode der Basis versteckt und gibt sich als UnitBuilder: public neue UnitBuilder withId (int id); –

+0

Ich stimme zu, aber zerstört das nicht die Verwendung der Basisklasse? – grmbl

+0

Es ist nicht, Sie verwenden immer noch die Basisklasse 'Methode, es ist nur Ihre nennen es explizit. Ich kann nicht sehen, wie du das sonst machen würdest. Sie brauchen Ihre 'WithId'-Methode, um den Typ zurückzugeben, der gerade von ihr erbt, Sie können das nicht haben. – GSerg

Antwort

2

Die Antwort ist einfach, Ihre Basisklasse-Builder-Methoden müssen zuletzt aufgerufen werden und können nicht mit Ihren spezifischeren Builder-Klassen verkettet werden, da sie das generische zurückgibt. Ändern Sie einfach Ihren Code in:

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

Das war's!

+1

danke! Das funktioniert und ich schäme mich, dass ich das selbst nicht versucht habe. Aber es fühlt sich irgendwie eingeschränkt an, trotzdem akzeptiere ich das als Antwort. – grmbl

+0

@grmbl Ich stimme zu, es ist restriktiv. Der Benutzer benötigt Kenntnisse darüber, welche fließenden Methoden in der Basisklasse und welche in der abgeleiteten Klasse beschrieben sind. Einige der SOLID-Designprinzipien brechen. – Ruskin

0

Das ist meine Arbeit Endlösung:

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

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

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

    public virtual Builder<TEntity, TBuilder> WithObjectState(ObjectState objectState) 
    { 
     this._objectState = objectState; 
     return this; 
    } 

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