2017-04-26 2 views
2

Ich habe ein Setup wie so:Parameter des Typs X muss Schnittstelle unterstützen Y

IBuilder = interface(IInvokable) 
end; 

IBuilder<T: IBuilder; TOut : TWinControl> = interface(IInvokable) 
end; 

TBuilder<T: IBuilder; TOut : TWinControl> = class(TInterfacedObject, IBuilder, IBuilder<T, TOut>) 
end; 

TBuilder = class(TBuilder<TBuilder, TWinControl>) 
end; 

Diese Art von Struktur ermöglicht es mir, wie so eine Zucker Syntax zu erstellen:

TBuilder<T : IBuilder; TOut : TWinControl> = class(TInterfacedObject, IBuilder, IBuilder<T, TOut>) 

    function Output : TOut; 
    function Name(aName : string) : T; 
    function Left(aLeft : Integer) : T; 
    function Top(aTop : Integer) : T; 

end; 

// ... later 

TBuilder.Create().Left(10).Top(5).Name('ABC'); // Nice one liner 

Das Problem ist, dass ich erhalte einen Übersetzungsfehler, sagen, dass

E2514 The type parameter TBuilder must support interface 'IBuilder'. 

Dies ist auf die typisierte Einschränkung T: IBuilder Prese wahrscheinlich ist nt auf der Schnittstelle, obwohl TBuilder IBuilder (durch seinen Vorfahren) unterstützt.

Kann mir bitte jemand sagen, wie ich das umgehen soll?

Obwohl, kann ich nicht TBuilder = class(TBuilder<IBuilder, TObject>)

Antwort

2

verwenden dies nicht getan werden kann. Sie versuchen, im Wesentlichen, dies zu tun:

IBar = interface(IInterface) end; 

    TFoo<T : IBar> = class(TObject, IBar) end; 

    TBar = TFoo<TBar>; 

Welche Fehler erzeugt

E2086 Type ‚TBar‘ noch nicht vollständig

Ohne die Schnittstelle Abhängigkeit definiert Sie dieses schreiben kann als

TBar = class(TFoo<TBar>) end; 

machen es zu einem echten Nachkomme und nicht nur ein ali wie. Dies könnte normalerweise den Typ auflösen, aber die Schnittstellenabhängigkeit zwingt den Compiler, die Frage zu stellen: Unterstützt TBarIBar?

Wenn man darüber nachdenkt, das funktioniert, wie:

TBar = TFoo<TBar> {TBar support IBar?} 
      | 
      TBar = TFoo<TBar>... {ok, TBar support IBar?} 
          | 
         TBar = TFoo<TBar> {ok, TBar support IBar?} 
             | 
             {...turtles all the way down} 

Sie fragen den Compiler eine unendliche Rekursion Problem zu lösen. Es kann das nicht tun.

+0

Wissen Sie, ob es einen Workaround gibt, der vielleicht einen Basistyp, das delphi "Gewohnheit" Muster oder etwas Ähnliches benutzt? – Ludo

+0

@Ludo Nichts kommt in den Sinn, aber persönlich würde ich diesen Ansatz einfach vermeiden, es sei denn, es bietet Ihnen wirklich einen materiellen Vorteil gegenüber etwas weniger Zucker.Wenn es einen greifbaren Vorteil bietet, gibt es wahrscheinlich einen besseren Weg, es zu implementieren. Schwer zu sagen, ohne mehr darüber zu wissen, wie das funktionieren soll. –

0

Sie können dies beheben, indem Sie den Rückgabetyp Ihrer Methoden ändern und den rekursiven Typparameter ausschließen.

interface 
type 
    //IBuilder = interface(IInvokable) 
    //end; //I don't think you need this 

    IBuilder<TOut : TWinControl> = interface(IInvokable) 
    function Output : TOut; 
    function Name(const aName : string) : IBuilder<TOut>; 
    function Left(aLeft : Integer) : IBuilder<TOut>; 
    function Top(aTop : Integer) : IBuilder<TOut>; 
    end; 

TFactory<TOut: TWinControl> = record 
    class function New: IBuilder<TOut>; static; 
end; 

implementation 
type  
    //Put the actual class in the implementation 
    TBuilder<TOut : TWinControl> = class(TInterfacedObject, IBuilder<TOut>) 
    //see interface 
    end; 

Sie dies in der Regel wie so verwenden:

var 
    MyButton: IBuilder<TButton>; 
begin 
    MyButton:= TFactory<TButton>.New.Left(10).Top(5).Name('ABC'); 

Wenn Sie Schnittstelle verwenden, dann sollten Sie nie mit der Klasse arbeiten, immer mit der Schnittstelle ausschließlich in Wechselwirkung treten. Indem Sie die Klassendefinition in der Implementierung verschieben, erzwingen Sie dies. Um das auszugleichen, fügen Sie eine Factory-Methode in die Schnittstelle ein.

In diesem Fall muss es sich um einen Datensatz handeln, da Sie (noch) keine generischen Standalone-Methoden haben können.

+0

Das Problem mit Ihrer Lösung ist, dass Sie Ihre "Builders" nicht erben können, zum Beispiel TComponentBuilder <| ---- TFormBuilder. In meinem Fall habe ich die Klassen, die ich erstellen möchte (TComponent, TWinControl, TForm usw.), in Buildern (TComponentBuilder, TWinControlBuilder, TFormBuilder) gespiegelt. Jede Builder-Klasse ist mit den Methoden angereichert (TComponentBuilder hat Name (aName: string), TWinControlBuilder hat Align (aAlign), TFormBuilder hat die Funktion FormStyle (aFormStyle) ... – Ludo

Verwandte Themen