我有这样的设置:
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
,即使TBuilder确实支持IBuilder(通过它的祖先)。
有人可以指导我如何解决这个问题吗?
但是,我无法使用TBuilder = class(TBuilder<IBuilder, TObject>)
答案 0 :(得分:2)
这无法完成。你实际上是在尝试这样做:
IBar = interface(IInterface) end;
TFoo<T : IBar> = class(TObject, IBar) end;
TBar = TFoo<TBar>;
哪会产生错误
E2086 Type&#39; TBar&#39;还没有完全定义
如果没有接口依赖,您可以将其写为
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}
您要求编译器解决无限递归问题。它无法做到这一点。
答案 1 :(得分: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;