2009-11-06 8 views
11

Ho iniziato a utilizzare dei farmaci generici in Delphi 2010, ma ho un problema quando si compila questo pezzo di codice:Perché i tipi TGeneric <Base> e TGeneric <Descendant> sono incompatibili?

TThreadBase = class(TThread) 
... 
end; 

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

TDataProviderThread = class(TThreadBase) 
... 
end; 

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

allora ho qualche procedura nidificato:

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

E infine voglio chiamare questa procedura nidificato nel codice della classe di TDataCore:

MakeAllThreadsActive(FProviders); 

Ma compilatore non vuole compilarlo e dice (' <>' parentesi sono sostituite da '()'):

[DCC errore] LSCore.pas (494): E2010 tipi incompatibili: 'TThreadBaseList (TThreadBase)' e 'TThreadBaseList (TDataProviderThread)'

Non lo capisco anche se TDataProviderThread è discendente di TThreadBase.

ho dovuto risolvere il problema da difficile fusione di caratteri:

MakeAllThreadsActive(TThreadBaseList<TThreadBase>(FProviders)); 

Qualcuno sa perché il compilatore dice che questo errore?

+1

Come altri hanno già spiegato PERCHÉ si ottiene questo errore, provare a rendere MakeAllThreadsActive un metodo di TThreadBaseList per risolvere il problema. –

risposta

22

TDataProviderThread è un discendente di TThreadBase, ma TThreadBaseList<TDataProviderThread> non è un discendente di TThreadBaseList<TThreadBase>. Questa non è ereditarietà, si chiama covariance, e anche se sembra intuitivamente la stessa cosa, non lo è e deve essere supportato separatamente. Al momento, Delphi non lo supporta, anche se si spera che sarà in una versione futura.

Ecco il motivo di base del problema di covarianza: se la funzione passata a è in attesa di un elenco di oggetti TThreadBase e si passa a un elenco di oggetti TDataProviderThread, non c'è nulla che impedisca di chiamare. Aggiungere e incollare alcuni altro oggetto TThreadBase nella lista che non è un TDataProviderThread e ora hai tutti i tipi di problemi brutti. Hai bisogno di trucchi speciali dal compilatore per assicurarti che ciò non possa accadere, altrimenti perdi la sicurezza del tuo tipo.

EDIT: Ecco una possibile soluzione per voi: Fare MakeAllThreadsActive in un metodo generico, in questo modo:

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

Oppure si potrebbe fare quello che Uwe Raabe suggerito. O uno funzionerà.

+2

+1 buona spiegazione, anche se conosco il termine * covarianza * principalmente dai metodi. – jpfollenius

+2

Se si sta solo leggendo dall'oggetto, allora è un ** sorgente ** e la covarianza è OK.Se invece stai scrivendo nuovi valori, hai un ** sink ** e vuoi controvarionalità. Se è sia una fonte che un sink (che dobbiamo assumere TObjectList è, se non abbiamo alcuna conoscenza intrinseca di ciò che uno qualsiasi dei suoi metodi fa), allora non puoi avere alcuna varianza. Imbucare il compilatore con quel concetto va ben oltre "trucchi speciali". –

+1

I trucchi speciali di cui sto parlando implicano l'aggiunta di supporto per covarianza e controvarianza al compilatore e quindi contrassegno i singoli metodi di TObjectList come sicuri per la covarianza o controvarianza in modo tale che il compilatore possa verificarlo. –

6

Il tipo

TList <TBase> 

non è il tipo di genitore

TList <TChild> 

Generics non può essere usato in questo modo.

Problemi correlati