类型X的参数必须支持接口Y.

时间:2017-04-26 14:24:04

标签: class delphi generics interface compiler-errors

我有这样的设置:

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

2 个答案:

答案 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;