2009-11-06 7 views
11

he empezado a utilizar de los genéricos en Delphi 2010, pero tengo un problema al compilar este pedazo de código:¿Por qué son TGeneric <Base> y TGeneric <Descendant> tipos incompatibles?

TThreadBase = class(TThread) 
... 
end; 

TThreadBaseList<T: TThreadBase> = class(TObjectList<T>) 
... 
end; 

TDataProviderThread = class(TThreadBase) 
... 
end; 

TDataCore = class(TInterfacedObject, IDataCore) 
private 
    FProviders: TThreadBaseList<TDataProviderThread>; 
... 
end; 

entonces tengo algún procedimiento anidado:

procedure MakeAllThreadsActive(aThreads: TThreadBaseList<TThreadBase>); 
begin 
... 
end; 

Y por último quiero llamar este procedimiento anidado en el código de la clase TDataCore:

MakeAllThreadsActive(FProviders); 

Pero compilador no quieren compilar y dice (' <>' soportes se sustituyen por '()'):

[Error DCC] LSCore.pas (494): E2010 tipos incompatibles: 'TThreadBaseList (TThreadBase)' y 'TThreadBaseList (TDataProviderThread)'

No lo entiendo aunque TDataProviderThread es descendiente de TThreadBase.

tuve que solucionarlo por encasillamiento duro:

MakeAllThreadsActive(TThreadBaseList<TThreadBase>(FProviders)); 

¿Alguien sabe por qué el compilador dice este error?

+1

Como otros ya explicaron POR QUÉ usted obtiene este error, intente hacer que MakeAllThreadsActive un método de TThreadBaseList para solucionarlo. –

Respuesta

22

TDataProviderThread es un descendiente de TThreadBase, pero TThreadBaseList<TDataProviderThread> no es un descendiente de TThreadBaseList<TThreadBase>. Eso no es herencia, se llama covarianza, y aunque parezca intuitivamente lo mismo, no lo es y debe admitirse por separado. Por el momento, Delphi no lo admite, aunque con suerte lo hará en una versión futura.

Aquí está la razón básica para el problema de covarianza: si la función a la que lo pasa espera una lista de objetos TThreadBase, y le pasa una lista de objetos TDataProviderThread, no hay nada que impida que llame. Agregue y pegue algunos otro objeto TThreadBase en la lista que no es un TDataProviderThread, y ahora tienes todo tipo de problemas desagradables. Necesita trucos especiales del compilador para asegurarse de que esto no pueda suceder, de lo contrario, perderá la seguridad de su tipo.

EDIT: He aquí una posible solución para usted: Hacer MakeAllThreadsActive en un método genérico, como esto:

procedure MakeAllThreadsActive<T: TThreadBase>(aThreads: TThreadBaseList<T>); 

O usted podría hacer lo que sugirió Uwe Raabe. Cualquiera de los dos funcionará.

+2

+1 buena explicación, aunque sé el término * covarianza * principalmente de los métodos. – jpfollenius

+2

Si solo lee el objeto, entonces es ** fuente ** y la covarianza es correcta.Si está escribiendo nuevos valores en su lugar, entonces tiene un ** sumidero ** y quiere contravarianza. Si se trata tanto de una fuente como de un sumidero (que debemos suponer que TObjectList es, si no tenemos conocimiento intrínseco de lo que hace cualquiera de sus métodos), entonces no puede haber ninguna varianza en absoluto. Imbuir al compilador con ese concepto va mucho más allá de los "trucos especiales". –

+1

Los trucos especiales de los que estoy hablando implican agregar soporte para la covarianza y la contravarianza al compilador, y luego marcar los métodos individuales de TObjectList como seguros para la covarianza o contravariancia de tal forma que el compilador pueda verificarlo. –

6

El tipo

TList <TBase> 

no es el tipo de matriz de

TList <TChild> 

genéricos no pueden ser utilizados de esa manera.

Cuestiones relacionadas