2009-07-23 13 views
9

Für jedes Setter einer Klasse Ich habe einige Ereignislogik (OnChanging, OnChanged) zu implementieren:Wiederholte Setter-Logik 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; 

Können Sie bitte einen Weg vorschlagen, dies zu implementieren, ohne für jede all diese Zeilen duplizieren Setter?

+1

+1 vor generelles Problem, dass Sie sehr finden oft in der Event-Bases-Programmierung. –

+2

Sie sollten zuerst überprüfen, dass Wert <> OldValue, es ist die übliche Redewendung in der VCL verwendet wird. Entweder am Anfang der Methode oder nach dem OnChanging-Ereignis (hängt davon ab, ob OnChanging einen var-Parameter erhält oder nicht, d. H. Ob der neue Wert geändert werden kann oder nicht). – mghie

Antwort

13

Versuchen Sie folgendes:

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

eigentlich - das ist das gleiche wie meine Lösung mit einer schöneren Syntax obwohl. –

+1

Nicht nur * schöner * Syntax, Tobias. * Gültige * Syntax. –

+0

Ich habe mich vertippt - ich habe nur den Typ anstelle des Variablennamens verwendet. Ich korrigierte mein Beispiel, ich hatte keinen Compiler zur Hand. Wie Sie sehen können, ist dies eine gültige Syntax. Die Idee bleibt gleich. Sie übergeben die Adresse (über Zeiger oder mit var). Der Compiler macht das gleiche. Auf einer binären Ebene ist das Ergebnis dasselbe - nur die Syntax ist netter, als ich vorher sagte. Es ist nicht noch einfacher zu benutzen. –

0

Sie könnten eine zusätzliche Methode hinzufügen. Etwas wie:

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; 

Ich habe den Code jedoch nicht kompiliert/getestet. Die Idee ist eine allgemeine Setter-Methode, die mit einem Zeiger auf den Ort arbeitet. Die spezialisierten Versionen rufen nur die Methode "generall" mit der Adresse der einzustellenden Variable auf. Sie müssen jedoch einen Typ von allgemeinen Setter-Methode pro Typ der Variablen (Double, Integer, ...) haben. Sie können es ändern, um mit Pointer und Länge einer Variablen zu arbeiten, um mit allen Typen zu arbeiten - Ihre Entscheidung, ob es sich lohnt.

+0

Anstelle eines Zeigers sollten Sie einen Var-Parameter wie in Cobus Krugers Vorschlag verwenden. – dummzeuch

+0

das ist nur syntaktischer Zucker, obwohl es schöner zu lesen ist. –

+1

@ Tobias: Nein, nicht nur syntaktischer Zucker. Mit dem var-Parameter ist es unmöglich, einen NULL-Zeiger zu übergeben, also muss die SetValue() -Methode nicht darauf achten (BTW, Ihr Code tut das nicht!). Eine API mit weniger Missbrauchspotenzial ist einfach besser. – mghie

7

Delphi unterstützt indiziert Eigenschaften. Mehrere Eigenschaften können durch einen Ordinalindex einen einzigen Getter oder Setter, differenziert teilen:

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; 

du mit Cobus's answer kombinieren, um diese:

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

Das könnte Sie Ideen für andere Wege geben, können Sie beziehen Sie sich auf Ihre Felder nach Index, anstatt zwei völlig separate Felder für die entsprechenden Werte zu haben.

+0

+1 sehr interessant! Ich war mir dessen nicht bewusst. – jpfollenius

+0

+1 für die Antwort, die ich fast gab, aber dachte, es wäre zu lang. Scheint, dass es immerhin noch kompakt beschrieben werden kann :-) –

0

wenn Prozedur/Funktion der Parameter sind die gleichen und Code zwischen Anfang und Ende gleich sind, dann können Sie nur

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

verwenden, die es ...