2013-10-22 17 views
7

Ciò sembrerebbe abbastanza semplice e forse mi sto manca solo un po 'di colla sintassi ... Qui è la mia semplice generico (Delphi XE3) Esempio:Come posso utilizzare un metodo generico in un tipo derivato

unit Unit1; 

interface 

uses 
    generics.collections; 

type 

X = class 
public 
    Id: Integer; 
end; 

XList<T : X> = class(TObjectList<T>) 
function Find(Id: Integer) : T; 
end; 

Y = class(X) 

end; 

YList = class(XList<Y>) 
end; 

implementation 

{ XList<T> } 

function XList<T>.Find(Id: Integer): T; 
var 
    t: X; 
begin 
    for t in Self do 
    if t.Id = Id then 
     Result := t; 
end; 

end. 

Questo non verrà compilato con "[Errore dcc32] Unità1.pas (41): E2010 Tipi incompatibili: 'Y' e 'X'". È giù alla linea:

YList = class(XList<Y>) 
end; 

Y deriva da X quindi perché c'è un problema?

risposta

6

ho dovuto reimplementare il metodo Find come segue per risolvere il problema:

{ XList<T> } 

function XList<T>.Find(Id: Integer): T; 
var 
    item: T; 
begin 
    for item in Self do 
    if item.Id = Id then 
     Exit(item); 
    Result := nil; 
end; 

cosa consigliarvi importante qui è quello di sostituire il tipo utilizzato nella dichiarazione delle variabili X-T.

Poi ho appena rinominato la variabile da t a item per evitare una collisione nome con il tipo di segnaposto T e raplaced il Result := item da Exit(item) restituire l'articolo trovato e chiudere il metodo.

+0

Grandi cose @AlexSC. Originariamente avevo 'var t: T'. Mi ero dimenticato di insensibilità al caso. La roba dei miei generici di C# è arrivata lì! – Rob

+0

Per inciso ... perché il lavoro originale non funziona? T è derivato da X. – Rob

+0

@Rob: mi dispiace, non ho una risposta per questo. Per me il compilatore ha individuato il messaggio di errore nell'ultima riga dell'unità, ciò che mi suggerisce che ha qualcosa a che fare con l'istanza di tipo generico. Sembra che il compilatore sia molto severo riguardo alla corrispondenza dei tipi quando si tratta di generici. – AlexSC

8

La risposta di Alex è la soluzione corretta al problema. E fa anche bene a tornare dalla funzione non appena la risposta è nota.

Mi piacerebbe ampliare la risposta con qualche spiegazione in più. In particolare vorrei rispondere alla domanda che hai posto nei commenti alla risposta di Alex:

Come una parte ... perché l'originale non funziona? T è derivata da X.

Il codice problema è qui:

function XList<T>.Find(Id: Integer): T; 
var 
    t: X; 
begin 
    for t in Self do 
    if t.Id = Id then 
     Result := t; 
end; 

Il modo di pensare farmaci generici, è immaginare quello che il codice sembra quando si crea un'istanza il tipo e la fornitura di un tipo concreto parametro. In questo caso, sostituiamo T con Y. Quindi il codice è simile:

function XList_Y.Find(Id: Integer): Y; 
var 
    t: X; 
begin 
    for t in Self do 
    if t.Id = Id then 
     Result := t; 
end; 

ora avete un problema alla linea che assegna a Result:

Result := t; 

Beh, Result è di tipo Y, ma t è di tipo X. La relazione tra X e Y è che Y deriva da X. Quindi un'istanza di Y è un X. Ma un'istanza di X non è un Y. E così il compito non è valido.

Come ha giustamente sottolineato Alex, è necessario dichiarare la variabile di loop di tipo T. Personalmente mi sento di scrivere il codice in questo modo:

function XList<T>.Find(Id: Integer): T; 
begin 
    for Result in Self do 
    if Result.Id = Id then 
     exit; 
    Result := nil; 
    // or perhaps you wish to raise an exception if the item cannot be found 
end; 

Questo affronta anche il problema che la ricerca di routine lascia il suo valore di ritorno inizializzato nel caso in cui l'articolo non viene trovato.Questo è un problema che il compilatore avrebbe avvertito circa una volta ottenuto il codice fino alla compilazione effettiva. Spero che tu abiliti gli avvertimenti del compilatore e gestirli quando appaiono!

Problemi correlati