2012-04-27 8 views
8

ジェネリック型を使用する私のコードに問題があります。コンパイラは、渡されたリスト(Result)がTObjectList<TItem>TItemのタイプはTの場合はTItems)であることを知りません。なぜTObjectList <S: T>をTObjectListを受け取る関数に渡すことができません<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; 

実装:エラーを持つ関数で

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; 

答えて

3

ResultTTItemのいくつかのサブクラスですが、コンパイラは何を知っていないTObjectList<T>、ありますそれは特定のクラスです。コンパイラは、の値がTの値になるようにコンパイルする必要があります。これは引数タイプがLoadAllTObjectList<TItem>が必要)と互換性がないため、コンパイラがコードを拒否します。

TTItemDescendantであり、コンパイラがエラーコードをコンパイルして実行できるとします。 LoadAllAList.Add(TItem.Create)を呼び出す場合、AListTObjectList<TItemDescendant>であっても、TItemDescendantでないものを保持することになります。その汎用型パラメータが保持しているとは異なる型のオブジェクトを保持しています。

ちょうどSがサブタイプTであるため、X<S>はサブタイプX<T>であるとは限りません。あなたにもローダのジェネリック版を使用する必要が

+0

そうだね、私はこちら側からそれを見ていませんでした。このコードをリファクタリングする方法はありますか?私がLoadAllのようなインターフェースを持ち、結果リストを返すか、または結果リストをTItems.LoadAllへのパラメータとして渡すことは重要です。しかし、実際の作業は 'TItemsLoader'によって行われています。 TItemsは間接レイヤーです。 TItemsは、どのような種類のアイテムが「知っている」かを知っています。 TItemsLoaderは、項目がTItemまたはその子孫であることのみを知るべきです。 – robertw

+1

私は 'IItemsLoader'インターフェースで他の答えがあると考えています。しかし、ロブはそれを正確に説明し、あなたの直接の質問に答えました。 –

4

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; 
+0

私の考えでは、 'loader 'はTItemだけで動作するはずです。しかし、私はそれを変更しようとします。 – robertw

+0

一般的な定義には制約があるため、TItemとその他のサブクラスでも動作します。 –

関連する問題