2010-12-11 14 views
8

Ho appena provato il mio primo utilizzo di generici in Delphi 2009 e sono perplesso su come utilizzare un tipo generico come input per la funzione Supports usata per vedere se un oggetto implementa un dato interfaccia. Ho creato un piccolo esempio che illustra il problema.Uso della funzione Supports() con interfaccia generica tipo

Dato il seguente tipo e funzione di utilità:

IMyInterface = interface 
['{60F37191-5B95-45BC-8C14-76633826889E}'] 
end; 

TMyObject = class(TInterfacedObject, IMyInterface) 
end; 

class function TFunctions.GetInterface<T>(myObject: TObject): T; 
var 
    specificInterface: T; 
begin 
    // This would compile, but looses the generic capability 
    //Supports(myObject, IMyInterface, specificInterface); 

    // This results in compile errors 
    Supports(myObject, T, specificInterface); 

    result := specificInterface; 
end; 

e il seguente frammento di codice:

class procedure TFunctions.Test; 
var 
    myObject: TMyObject; 
    myInterface: IMyInterface; 
begin 
    myObject := TMyObject.Create; 

    myInterface := GetInterface<IMyInterface>(myObject); 
end; 

mi si aspetterebbe problemi ma ottengo i seguenti errori in fase di compilazione:

[Errore DCC] GenericExample.pas (37): E2029 '(' previsto ma ',' trovato [Errore DCC] Generazione icExample.pas (37): E2014 Dichiarazione previsto, ma l'espressione di tipo 'T' trovato

io non sono sicuro di quello che il compilatore mi sta aspettando a che fare con la T quando viene utilizzato come argomento attuale della funzione .

Ho cercato in giro un po 'e non sono stato in grado di rompere questo. Una parte di me sospetta che se potessi capire come un nome di interfaccia viene convertito nel tipo IID: TGUID durante la compilazione, quando si utilizza un nome di interfaccia concreta, potrei fare qualche progresso, ma anche questo mi ha eluso.

Qualsiasi aiuto è molto apprezzato.

risposta

7

Non vi è alcuna garanzia che T abbia un GUID ad esso associato e non ci sono mezzi nella lingua per scrivere un vincolo sul parametro di tipo per garantire tale garanzia.

Il nome dell'interfaccia viene convertito in un GUID dal compilatore che ricerca il nome nella tabella dei simboli, ottiene la struttura dei dati del compilatore che rappresenta l'interfaccia e controlla il campo corrispondente per il GUID. Ma i generici non sono come i modelli C++; devono essere compilati e controllati dal tipo e noti per funzionare con qualsiasi parametro di tipo valido e ciò significa limitare il parametro type nella sua dichiarazione.

È possibile ottenere il GUID utilizzando RTTI (innanzitutto verificare che T rappresenti effettivamente un'interfaccia) con qualcosa come GetTypeData(TypeInfo(T))^.Guid e passare il GUID a Supports in questo modo.

+0

non si può applicare un vincolo che T deve essere un'interfaccia specificata con un GUID? –

+1

Se deve essere presente un'interfaccia, il codice non è generico. I supporti restituiranno un riferimento all'interfaccia; il compilatore non può genericamente trasformarlo in qualunque T sia. –

+1

Barry, grazie per il tuo aiuto. La maggior parte della mia esperienza con la programmazione generica è derivata da uno sfondo C++, e mi aspettavo che il compilatore sapesse se un'interfaccia ha un Guid nello stesso modo in cui avrebbe saputo se il nome dell'interfaccia fosse stato fornito direttamente. Ha perfettamente senso ora sapendo che i modelli generici <> C++. Grazie ancora. – Chad

3

Perché ti stai preoccupando?

Per utilizzare questa TFunctions.GetInterface è necessario:

  • un'interfaccia
  • un riferimento all'oggetto

Se si dispone di questi, allora si può chiamare Supporti() direttamente:

intf := TFunctions.GetInterface<IMyInterface>(myObject); 

è esattamente equivale nt a:

Supports(IMyInterface, myObject, intf); 

Utilizzando farmaci generici qui è uno spreco di tempo e fatica e realmente pone la domanda: "Perché farlo?".

Sta solo rendendo le cose più difficili da leggere (come spesso accade con i generici) ed è più complicato da usare.

supporti() restituisce una comoda booleano per indicare il successo/insuccesso, che si deve verificare a parte tramite il vostro involucro:

intf := TFunctions.GetInterface<IMyInterface>(myObject); 
    if Assigned(intf) then 
    // ... 

versus:

if Supports(IMyInterface, myObject, intf) then 
    // We can use intf 

Durante la creazione di wrapper intorno funzionalità è generalmente il caso in cui il risultato è un miglioramento della leggibilità o dell'usabilità.

imho questo non riesce su entrambi i conteggi e si dovrebbe semplicemente rimanere con la funzione Supports() stessa.

+0

Il mio esempio viene distillato dal contesto attuale, per fare una domanda specifica e non è un esempio del design attuale. – Chad

+2

Quindi è necessario fornire almeno ALCUN contesto. Altrimenti è come chiedere come ottenere da A a B senza menzionare che non si può guidare e non si hanno soldi per una tariffa di autobus. Può darsi che anche nel contesto, quello che stai facendo non sia il modo migliore/più semplice per farlo. Va tutto bene e fa una domanda specifica, ma se vuoi delle risposte utili, allora non serve gettare un contesto importante. – Deltics

+2

Sono rispettosamente in disaccordo. Saluti. – Chad

Problemi correlati