2009-07-23 11 views
9

Per ogni setter di una classe devo implementare una logica evento (OnChanging, OnChanged):setter ripetute in Delphi

procedure TBlock.SetWeightIn(const Value: Double); 
var OldValue: Double; 
begin 
    OldValue := FWeightIn; 
    DoOnChanging(OldValue, Value); 
    FWeightIn := Value; 
    DoOnChanged(OldValue, Value); 
end; 

procedure TBlock.SetWeightOut(const Value: Double); 
var OldValue: Double; 
begin 
    OldValue := FWeightOut; 
    DoOnChanging(OldValue, Value); 
    FWeightOut := Value; 
    DoOnChanged(OldValue, Value); 
end; 

ti invitiamo a suggerire un modo per implementare questo senza duplicare tutte queste linee per ogni setter?

+1

+1 vor problema generale che troverete molto spesso nella programmazione di basi di eventi. –

+2

Dovresti prima controllare Valore <> OldValue, è il solito idioma usato in tutto il VCL. O all'inizio del metodo, o dopo l'evento OnChanging (dipende dal fatto che OnChanging ottenga o meno un parametro var, cioè se possa cambiare o meno il nuovo valore). – mghie

risposta

13

Prova questo:

procedure TBlock.SetField(var Field: Double; const Value: Double); 
var 
    OldValue: Double; 
begin 
    OldValue := Field; 
    DoOnChanging(OldValue, Value); 
    Field := Value; 
    DoOnChanged(OldValue, Value); 
end; 

procedure TBlock.SetWeightIn(const Value: Double); 
begin 
    SetField(FWeightIn, Value); 
end; 

procedure TBlock.SetWeightOut(const Value: Double); 
begin 
    SetField(FWeightOut, Value); 
end; 
+0

in realtà - è la stessa soluzione con una sintassi più bella però. –

+1

Non solo sintassi * più bella *, Tobias. * Sintassi valida *. –

+0

Ho scritto male - Ho appena usato il tipo al posto del nome della variabile. Ho corretto il mio esempio che non avevo alcun compilatore a portata di mano. Come puoi vedere è una sintassi valida. L'idea rimane la stessa. Si passa l'indirizzo (tramite puntatore o usando var). Il compilatore fa lo stesso. A livello binario il risultato è lo stesso - solo la sintassi è più bella di come ho detto prima. Non è ancora più facile da usare. –

0

È possibile aggiungere un metodo aggiuntivo. Qualcosa di simile:

procedure TBlock.setValue(const Value : Double; Location : PDouble); 
var 
    OldValue : Double; 
begin 
    OldValue:=Location^; 
    DoOnChanging(OldValue,Value); 
    Location^:=Value; 
    DOnChanged(OldValue, Value); 
end; 

procedure TBlock.setWeightOut(const Value : Double); 
begin 
    setValue(value, @FWeightOut); 
end; 

Non ho ancora compilato/testato il codice. L'idea è di avere un metodo setter generale che funzioni con un puntatore alla posizione. Le versioni specializzate chiamano semplicemente il metodo gerneral con l'indirizzo della variabile da impostare. Però devi avere un tipo di metodo setter generale per tipo di variabile (double, intero, ...). Puoi modificarlo per lavorare su Pointer e la lunghezza di una variabile per funzionare con tutti i tipi - la tua decisione se ne vale la pena.

+0

Piuttosto che un puntatore dovresti usare un parametro var come nel suggerimento di Cobus Kruger. – dummzeuch

+0

è solo zucchero sintattico, sebbene sia più piacevole da leggere. –

+1

@Tobias: No, non solo zucchero sintattico. Con il parametro var è impossibile passare un puntatore NULL, quindi il metodo SetValue() non deve controllarlo (a proposito, il tuo codice no!). Un'API con meno potenziale di abuso è semplicemente migliore. – mghie

7

Delphi supporta proprietà indicizzate. proprietà multipli possono condividere una singola getter o setter, differenziato da un indice ordinale:

type 
    TWeightType = (wtIn, wtOut); 
    TBlock = class 
    private 
    procedure SetWeight(Index: TWeightType; const Value: Double); 
    function GetWeight(Index: TWeightType): Double; 
    public 
    property InWeight: Double index wtIn read GetWeight write SetWeight; 
    property OutWeight: Double index wtOut read GetWeight write SetWeight; 
    end; 

È possibile combinare questo con Cobus's answer per ottenere questo:

procedure TBlock.SetWeight(Index: TWeightType; const Value: Double); 
begin 
    case Index of 
    wtIn: SetField(FWeightIn, Value); 
    wtOut: SetField(FWeightOut, Value); 
    end; 
end; 

Questo potrebbe darvi idee per altri modi si può fai riferimento ai tuoi campi per indice invece di avere due campi completamente separati per tali valori correlati.

+0

+1 molto interessante! Non ero a conoscenza di questo. – jpfollenius

+0

+1 per la risposta che ho quasi dato ma ho pensato che sarebbe stato troppo lungo. Sembra che possa ancora essere descritto in modo compatto dopotutto :-) –

0

se i parametri di procedura/di funzione sono uguali e codice tra inizio e fine sono gli stessi, allora si può semplicemente usare

procedure SetWeightValue(const Value: Double); 
var OldValue: Double; 
begin 
    OldValue := FWeightIn; 
    DoOnChanging(OldValue, Value); 
    FWeightIn := Value; 
    DoOnChanged(OldValue, Value); 
end; 

Questo è tutto ...