Delphi 为什么可以';我是否传递一个对象列表<;S:T>;对于需要TObjectList的函数<;T>;?

Delphi 为什么可以';我是否传递一个对象列表<;S:T>;对于需要TObjectList的函数<;T>;?,delphi,generics,tobjectlist,Delphi,Generics,Tobjectlist,我的代码有问题,它使用泛型类型。为什么编译器不知道传递的列表(Result)是TObjectList(TItem是TItems中t的类型) 接口: type TItem = class end; type IItemsLoader = interface procedure LoadAll(AList : TObjectList<TItem>); end; type TItemsLoader = class(TInterfacedObject, IItems

我的代码有问题,它使用泛型类型。为什么编译器不知道传递的列表(
Result
)是
TObjectList
TItem
TItems
t
的类型)

接口:

type
   TItem = class
end;

type
  IItemsLoader = interface
    procedure LoadAll(AList : TObjectList<TItem>);
end;

type
  TItemsLoader = class(TInterfacedObject, IItemsLoader)
public
  procedure LoadAll(AList : TObjectList<TItem>);
end;

type
  IItems<T : TItem> = interface
  function LoadAll : TObjectList<T>;
end;

type
  TItems<T : TItem> = class(TInterfacedObject, IItems<T>)
  private
    FItemsLoader : TItemsLoader;
  public
    constructor Create;
    destructor Destroy; override;
    function LoadAll : TObjectList<T>;
end;
类型
TItem=类
结束;
类型
IItemsLoader=接口
程序LoadAll(列表:TObjectList);
结束;
类型
TItemsLoader=class(TInterfacedObject,IItemsLoader)
公众的
程序LoadAll(列表:TObjectList);
结束;
类型
IItems=接口
函数LoadAll:TObjectList;
结束;
类型
TItems=类别(TInterfacedObject,IItems)
私有的
FItemsLoader:TItemsLoader;
公众的
构造函数创建;
毁灭者毁灭;推翻
函数LoadAll:TObjectList;
结束;
实施:

procedure TItemsLoader.LoadAll(AList: TObjectList<TItem>);
begin
  /// some stuff with AList
end;

{ TItems<T> }

constructor TItems<T>.Create;
begin
  FItemsLoader := TItemsLoader.Create;
end;

destructor TItems<T>.Destroy;
begin
  FItemsLoader.Free;
  inherited;
end;

function TItems<T>.LoadAll: TObjectList<T>;
begin
  Result := TObjectList<T>.Create();

  /// Error here
  /// FItemsLoader.LoadAll(Result);
end;
过程TItemsLoader.LoadAll(列表:TObjectList);
开始
///一些关于阿利斯特的东西
结束;
{TItems}
构造函数TItems.Create;
开始
FItemsLoader:=TItemsLoader.Create;
结束;
破坏分子滴度破坏;
开始
FItemsLoader.免费;
继承;
结束;
函数TItems.LoadAll:TObjectList;
开始
结果:=TObjectList.Create();
///这里出错
///FItemsLoader.LoadAll(结果);
结束;

在有错误的函数中,
结果是一个
TObjectList
,其中
T
TItem
的某个子类,但编译器不知道它是什么特定类。编译器必须对其进行编译,以便可以安全地运行
T
的任何值。这可能与
LoadAll
的参数类型不兼容,该类型需要
TObjectList
,因此编译器拒绝该代码

假设
T
titemdescendat
,编译器允许错误代码编译和执行。如果
LoadAll
调用
AList.Add(TItem.Create)
,那么
AList
将最终保存的东西不是
titemdescentant
,即使它是
TObjectList
。它保存的对象的类型与其泛型类型参数所表示的不同


仅仅因为
S
T
的子类型并不意味着
X
X
的子类型,您还必须使用通用版本的加载程序:

type
   TItem = class
end;

type
  IItemsLoader<T: TItem> = interface
    procedure LoadAll(AList : TObjectList<T>);
end;

type
  TItemsLoader<T: TItem> = class(TInterfacedObject, IItemsLoader<T>)
public
  procedure LoadAll(AList : TObjectList<T>);
end;

type
  IItems<T : TItem> = interface
  function LoadAll : TObjectList<T>;
end;

type
  TItems<T : TItem> = class(TInterfacedObject, IItems<T>)
  private
    FItemsLoader : TItemsLoader<T>;
  public
    constructor Create;
    destructor Destroy; override;
    function LoadAll : TObjectList<T>;
end;


implementation

{$R *.dfm}

procedure TItemsLoader<T>.LoadAll(AList: TObjectList<T>);
begin
  /// some stuff with AList
end;

{ TItems<T> }

constructor TItems<T>.Create;
begin
  FItemsLoader := TItemsLoader<T>.Create;
end;

destructor TItems<T>.Destroy;
begin
  FItemsLoader.Free;
  inherited;
end;

function TItems<T>.LoadAll: TObjectList<T>;
begin
  Result := TObjectList<T>.Create();

  /// Error here
  FItemsLoader.LoadAll(Result);
end;
类型
TItem=类
结束;
类型
IItemsLoader=接口
程序LoadAll(列表:TObjectList);
结束;
类型
TItemsLoader=class(TInterfacedObject,IItemsLoader)
公众的
程序LoadAll(列表:TObjectList);
结束;
类型
IItems=接口
函数LoadAll:TObjectList;
结束;
类型
TItems=类别(TInterfacedObject,IItems)
私有的
FItemsLoader:TItemsLoader;
公众的
构造函数创建;
毁灭者毁灭;推翻
函数LoadAll:TObjectList;
结束;
实施
{$R*.dfm}
程序TItemsLoader.LoadAll(列表:TObjectList);
开始
///一些关于阿利斯特的东西
结束;
{TItems}
构造函数TItems.Create;
开始
FItemsLoader:=TItemsLoader.Create;
结束;
破坏分子滴度破坏;
开始
FItemsLoader.免费;
继承;
结束;
函数TItems.LoadAll:TObjectList;
开始
结果:=TObjectList.Create();
///这里出错
FItemsLoader.LoadAll(结果);
结束;

你说得对,我没有从这边看。你对如何重构这段代码有什么建议吗?对我来说,像LoadAll和returnresult list这样的接口很重要,或者将result list作为参数传递给TItems.LoadAll,但真正的工作是通过
TItemsLoader
完成的。滴度只是间接层。TItems“知道”会有什么样的物品。TItemsLoader应该只知道,项目是TItem或其后代。我相信使用
IItemsLoader
界面的另一个答案是您需要改变的。然而,罗布已经正确地解释了这一点,并回答了你的直接问题。在我的概念中,“加载器”应该只适用于TItem。但是我将尝试改变它,它将与TItem以及其他任何子类一起工作,因为泛型定义具有约束。