2012-04-27 3 views
8

Tengo un problema con mi código, que usa tipos genéricos. ¿Por qué el compilador no sabe que la lista aprobada (Result) es TObjectList<TItem> (TItem es tipo para T en TItems)?¿Por qué no puedo pasar una TObjectList <S: T> a una función que espera una TObjectList <T>?

Interfaz:

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; 

Implementación:

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; 

Respuesta

3

En la función con el error, es una ResultTObjectList<T>, donde T alguna subclase de TItem, pero el compilador no sabe qué clase específica es. El compilador tiene que compilarlo para que sea seguro ejecutar cualquier valor de T. Puede que no sea compatible con el tipo de argumento de LoadAll, que requiere un TObjectList<TItem>, por lo que el compilador rechaza el código.

Supongamos que T es T, y el compilador permite compilar y ejecutar el código defectuoso. Si LoadAll llama al AList.Add(TItem.Create), entonces AList terminará sosteniendo algo que no es un TItemDescendant, aunque es un TObjectList<TItemDescendant>. Tiene un objeto de un tipo diferente de lo que dice su parámetro de tipo genérico.

El hecho de que S es un subtipo de T no significa que X<S> es un subtipo de X<T>.

+0

Tienes razón, no lo miramos desde este lado. ¿Tienes alguna sugerencia de cómo refactorizar este código? Para mí es importante tener una interfaz como LoadAll y devolver la lista de resultados o pasar la lista de resultados como parámetro a TItems.LoadAll. Pero el trabajo real está haciendo por 'TItemsLoader'. TItems es solo una capa indirecta. TItems "sabe" qué tipo de artículos tendrá. TItemsLoader solo debe saber que los elementos son TItem o su descendiente. – robertw

+1

Creo que la otra respuesta con la interfaz 'IItemsLoader' es lo que necesita hacer. Sin embargo, Rob lo ha explicado exactamente y respondió su pregunta directa. –

4

usted tiene que utilizar una versión genérica del cargador, así:

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

En mi concepto 'cargador' debería funcionar solo en TItem. Pero intentaré cambiarlo. – robertw

+0

Funcionará con TItem y también con cualquier otra subclase, ya que la definición genérica tiene la restricción. –

Cuestiones relacionadas