Por que Generic e Generic tipos incompatíveis?
-
18-09-2019 - |
Pergunta
eu comecei a usar de genéricos no Delphi 2010, mas eu tenho um problema quando compilar este pedaço de código:
TThreadBase = class( TThread )
...
end;
TThreadBaseList<T: TThreadBase> = class( TObjectList<T> )
...
end;
TDataProviderThread = class( TThreadBase )
...
end;
TDataCore = class( TInterfacedObject, IDataCore )
private
FProviders: TThreadBaseList<TDataProviderThread>;
...
end;
Então eu tenho algum procedimento aninhada:
procedure MakeAllThreadsActive(aThreads: TThreadBaseList<TThreadBase>);
begin
...
end;
E, finalmente, eu quero chamar este procedimento aninhado no código de classe TDataCore:
MakeAllThreadsActive(FProviders);
Mas compilador não quer compilar-lo e ele diz ( '<>' colchetes são substituídos por '()'):
[DCC ERRO] LSCore.pas (494): tipos E2010 incompatíveis: 'TThreadBaseList (TThreadBase)' e 'TThreadBaseList (TDataProviderThread)'
Eu não entendo isso, embora TDataProviderThread é descendente de TThreadBase.
Eu tive que corrigi-lo por typecasting duro:
MakeAllThreadsActive(TThreadBaseList<TThreadBase>(FProviders));
Alguém sabe por que o compilador diz esse erro?
Solução
TDataProviderThread é um descendente de TThreadBase, mas TThreadBaseList<TDataProviderThread>
não é um descendente de TThreadBaseList<TThreadBase>
. Isso não é herança, ele é chamado covariância , e embora pareça intuitivamente como a mesma coisa, não é e tem de ser apoiada separadamente. No momento, Delphi não apoiá-lo, embora espero que em uma versão futura.
Aqui está a razão básica para o problema covariância: Se a função de passá-lo para está esperando uma lista de TThreadBase objetos, e você passá-lo uma lista de TDataProviderThread objetos, não há nada para impedi-lo de chamar .Add e aderindo algum outro TThreadBase objeto na lista que não é um TDataProviderThread, e agora você tem todos os tipos de problemas feios. Você precisa de truques especiais do compilador para garantir que isso não pode acontecer, caso contrário, você perde o seu tipo de segurança.
EDIT: Aqui está uma solução possível para você: Faça MakeAllThreadsActive em um método genérico, como este:
procedure MakeAllThreadsActive<T: TThreadBase>(aThreads: TThreadBaseList<T>);
Ou você poderia fazer o que Uwe Raabe sugeriu. Qualquer um vai trabalhar.
Outras dicas
O tipo
TList <TBase>
não é o tipo de pai
TList <TChild>
Os genéricos não pode ser usado dessa forma.