2017-04-26 56 views
2

我有一個設置,如下所示:參數X型的必須支持接口Ÿ

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; 

這種結構使我能夠建立一個糖的語法像這樣:

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 

的問題是,我得到一個編譯錯誤,說

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

這可能是由於該類型的約束T: IBuilder prese nt在界面上,即使TBuilder確實支持IBuilder(通過它的祖先)。

任何人都可以請指導我如何解決這個問題?

雖然,我不能用TBuilder = class(TBuilder<IBuilder, TObject>)

回答

2

這不能做。你基本上試圖做到這一點:

IBar = interface(IInterface) end; 

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

    TBar = TFoo<TBar>; 

產生錯誤

E2086型「TBAR」尚未完全確定

沒有接口依賴,你可以寫爲

TBar = class(TFoo<TBar>) end; 

使它成爲真正的後代而不僅僅是阿里如。這通常可以解決該類型,但接口依賴性迫使編譯器提出以下問題:請問TBar是否支持IBar

如果你仔細想想,這個作品出來爲:

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} 

你問的編譯器來解決無限遞歸問題。它不能做到這一點。

+0

你知道是否有一種解決方法使用可能的基本類型,delphi「自定義」模式或類似的東西嗎? – Ludo

+0

@盧多沒有什麼可以想到的,但是我個人完全不採用這種方法,除非它確實爲您提供了一些對於一些小糖的實質性益處。如果它提供了一些實際的好處,那麼可能有更好的方法來實現它。很難說如果不知道更多關於你想如何工作。 –

0

您可以通過更改方法的返回類型並排除遞歸類型參數來解決此問題。

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; 

您通常使用像這樣:

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

如果你使用的接口,那麼你不應該與類的工作,總是以完全交互的界面。通過在實現中移動類定義,可以強制執行此操作。爲了補償你在界面中添加一個工廠方法。

在這種情況下,它必須是一個記錄,因爲你還不能擁有通用的獨立方法。

class function TFactory<TOut>.New: IBuilder<TOut>; 
begin 
    Result:= TBuilder<TOut>.Create; 
end; 
+0

解決方案的問題在於,如果無法繼承「構建器」(例如TComponentBuilder <| ---- TFormBuilder)。在我的情況下,我在構建器(TComponentBuilder,TWinControlBuilder,TFormBuilder)中鏡像我想要構建的類(TComponent,TWinControl,TForm等)。每個構建器類都有豐富的方法(TComponentBuilder有Name(aName:string),TWinControlBuilder有Align(aAlign),TFormBuilder有函數FormStyle(aFormStyle)... – Ludo

相關問題