2009-11-23 15 views
5

che sto cercando di scrivere un generico di accesso di proprietà nella cache come il seguente, ma sto ottenendo un errore di compilazione quando si cerca di verificare se la variabile di archiviazione contiene già un valore:Come si verifica una variabile di tipo generico per l'uguaglianza con Default (T) in Delphi?

function TMyClass.GetProp<T>(var ADataValue: T; const ARetriever: TFunc<T>): T; 
begin 
    if ADataValue = Default(T) then // <-- compiler error on this line 
    ADataValue := ARetriever(); 
    Result := ADataValue; 
end; 

L'errore che sto ottenendo è " E2015 Operatore non applicabile a questo tipo di operando ".

Dovrei mettere un vincolo su T per farlo funzionare? Il file della guida dice che Default() accetta qualsiasi cosa tranne i tipi generici. Nel mio caso mi occupo principalmente di tipi semplici come String, Integer e TDateTime.

Oppure c'è qualche altra funzione di libreria per eseguire questo controllo particolare?

Sto utilizzando Delphi 2009 nel caso che importi.


PS: Nel caso in cui non è chiaro dal codice quello che sto cercando di fare: Nel mio caso la determinazione dei valori delle proprietà attuali potrebbe richiedere del tempo per vari motivi e qualche volta non potrebbe nemmeno bisogno loro affatto. D'altro canto, i valori sono costanti, quindi desidero solo chiamare il codice che determina il valore effettivo la prima volta che accede a questa proprietà e quindi archiviare il valore in un campo classe e al successivo accesso a questa proprietà restituisce il valore memorizzato nella cache direttamente. Ecco un esempio di come speravo di essere in grado di utilizzare quel codice:

type 
    TMyClass = class 
    private 
    FSomeProp: String; 
    function GetSomeProp: String; 

    function GetProp<T>(var ADataValue: T; const ARetriever: TFunc<T>): T; 
    public 
    property SomeProp read GetSomeProp; 
    end; 

function GetSomeProp: String; 
begin 
    Result := GetProp<String>(FSomeProp, 
          function: String 
          begin 
           Result := SomeSlowOrExpensiveCalculation; 
          end); 
end; 

(ovviamente, c'è più di una sola proprietà)

+0

+1 bella idea a proposito. – jpfollenius

+1

Probabilmente dovresti usare un tipo nullable, altrimenti il ​​codice rivaluterebbe costantemente le proprietà già memorizzate nella cache ma con il valore predefinito. Vedi per esempio http://blogs.embarcadero.com/abauer/2008/09/18/38869 – mghie

+0

@mghie: buon punto e in genere sarei d'accordo ma in questo caso particolare il valore predefinito può essere sempre interpretato come "non inizializzato" . –

risposta

10

Dopo un suggerimento nei commenti da Binis e scavare intorno un po 'in Generics.Collections mi si avvicinò con la seguente, che sembra funzionare proprio come lo volevo :

function TMyClass.GetProp<T>(var ADataValue: T; const ARetriever: TFunc<T>): T; 
var 
    lComparer: IEqualityComparer<T>; 
begin 
    lComparer := TEqualityComparer<T>.Default; 
    if lComparer.Equals(ADataValue, Default(T)) then 
    ADataValue := ARetriever(); 
    Result := ADataValue; 
end; 
+2

Vale la pena notare che" IEqualityComparer "è ora all'interno di Generics.Defaults (non è sicuro da quando, usando Berlin 10.1) – Zulukas

-1

Il problema non è la funzione Default, ma l'operatore di uguaglianza = .

Si potrebbe limitare T-IEquatable e utilizzare il metodo Equals in questo modo:

TMyClass = class 
    function GetProp<T : IEquatable<T>>(var ADataValue: T; const ARetriever: 
end; 
... 
function TMyClass.GetProp<T>(var ADataValue: T; const ARetriever: TFunc<T>): T; 
begin 
if ADataValue.Equals (Default(T)) then 
    ADataValue := ARetriever(); 
Result := ADataValue; 
end; 
+0

Non so perché, ma ottengo un "identificatore non dichiarato 'IEquatable'" con quel codice (anche se posso vedere che 'IEquatable' è effettivamente dichiarato in System.pas). Tuttavia dubito che ciò funzionerebbe comunque perché non sono a conoscenza del fatto che tipi primitivi come 'String',' TDateTime' o 'Integer' in qualche modo supportano implicitamente quell'interfaccia ... –

+0

@Oliver: hai ragione, puoi ' lo uso per i tipi primitivi. Sfortunatamente, non sono a conoscenza di un'altra soluzione. Vediamo se gli altri sono in grado di aiutare. Non so perché ricevi un messaggio di errore quando usi 'IEquatable'. – jpfollenius

+0

In realtà, ciò che è dichiarato in System.pas non è 'IEquatable' ma' IEquatable ' che spiega l'errore ... 'GetProp > (...' compila ma poi, come previsto, ottengo l'errore quando si passa 'String' per' T': "Il parametro di tipo 'T' deve supportare l'interfaccia IEquatable " –

1

quanto ho capito le cose sembra che u vuole fare qualche tipo di funzionalità Memoize. Se questo è il caso appena letto questo articolo

http://blogs.teamb.com/craigstuntz/2008/10/01/37839/

+0

Grazie! Questo è davvero molto simile a quello che sto cercando di fare ma l'implementazione di Craig si tradurrebbe in troppo grande un sovraccarico non necessario nel mio caso particolare perché creerebbe un'istanza di una nuova istanza 'TDictionary' per ogni proprietà ... Purtroppo non è nemmeno una risposta alla domanda originale su come verificare se una variabile generica tipizzata ha un valore non predefinito oppure no, quindi, dato il contesto, temo di non aver nemmeno potuto revocare la tua risposta. :( –

+0

Puoi evitare questo overhead usando qualcosa come un 'TCacheManager'. Il gestore cache ha un dizionario y. Basta usare qualcosa come IntToStr (Integer (Pointer (Self))) + '.Property1'' come chiave e registrare i valori memorizzati nella cache. – jpfollenius

+3

Beh, l'unico modo "legale" per confrontare i farmaci generici è fare ciò che Delphi fa in Generics.Collections.pas. Dai un'occhiata all'implementazione di QuickSort. – Binis

Problemi correlati