【问题标题】:Why can't I pass a TObjectList<S: T> to a function expecting a TObjectList<T>?为什么我不能将 TObjectList<S: T> 传递给需要 TObjectList<T> 的函数?
【发布时间】:2012-04-28 05:46:42
【问题描述】:

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

界面:

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;

实施:

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;

【问题讨论】:

    标签: delphi generics tobjectlist


    【解决方案1】:

    您还必须使用通用版本的加载器:

    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;
    

    【讨论】:

    • 在我的概念中,`loader' 应该只适用于 TItem。但我会尝试改变它。
    • 它将与 TItem 以及任何其他子类一起使用,因为通用定义具有约束。
    【解决方案2】:

    在有错误的函数中,Result 是一个TObjectList&lt;T&gt;,其中TTItem 的某个子类,但编译器不知道它是什么具体的类。编译器必须对其进行编译,以便对于Tany 值可以安全运行。这可能与需要TObjectList&lt;TItem&gt;LoadAll 的参数类型不兼容,因此编译器会拒绝该代码。

    假设TTItemDescendant,编译器允许错误代码编译执行。如果LoadAll 调用AList.Add(TItem.Create),那么AList 将最终持有不是TItemDescendant 的东西,即使它是TObjectList&lt;TItemDescendant&gt;。它所持有的对象的类型与其泛型类型参数所说的不同。

    仅仅因为ST 的子类型并不意味着X&lt;S&gt;X&lt;T&gt; 的子类型。

    【讨论】:

    • 你说得对,我没有从这边看。您对如何重构此代码有任何建议吗?拥有像 LoadAll 这样的接口并返回结果列表或将结果列表作为参数传递给 TItems.LoadAll 对我来说很重要,但真正的工作是由TItemsLoader 完成的。 TItems 只是间接层。 TItems“知道”将有什么样的项目。 TItemsLoader 应该只知道项目是 TItem 或其后代。
    • 我相信IItemsLoader 接口的另一个答案是您需要更改的操作。然而,Rob 已经完全正确地解释了它并回答了您的直接问题。
    猜你喜欢
    • 2011-09-24
    • 1970-01-01
    • 1970-01-01
    • 2017-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-06
    • 1970-01-01
    相关资源
    最近更新 更多