2009-07-23 6 views
9

Pour chaque poseur d'une classe que je dois mettre en œuvre une logique d'événement (OnChanging, OnChanged):logique de setters répétées dans 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; 

Pouvez-vous s'il vous plaît suggérer une façon de mettre en œuvre ce sans dupliquer toutes ces lignes pour chaque setter?

+1

+1 vor problème général que vous trouverez très souvent dans la programmation des bases d'événements. –

+2

Vous devriez d'abord vérifier que Value <> OldValue, c'est l'idiome habituel utilisé dans la VCL. Soit au début de la méthode, soit après l'événement OnChanging (selon que OnChanging obtient ou non un paramètre var, c'est-à-dire s'il peut ou non changer la nouvelle valeur). – mghie

Répondre

13

Essayez ceci:

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

réellement - c'est la même chose que ma solution avec une syntaxe plus agréable cependant. –

+1

Pas seulement la syntaxe * nicer *, Tobias. * Syntaxe * valide * –

+0

J'ai mal tapé - Je viens d'utiliser le type au lieu du nom de la variable. J'ai corrigé mon exemple, je n'avais pas de compilateur sous la main. Comme vous pouvez le voir, c'est une syntaxe valide. L'idée reste la même. Vous passez l'adresse (via un pointeur ou en utilisant var). Le compilateur fait la même chose. Au niveau binaire, le résultat est le même - juste la syntaxe est plus belle que je l'ai déjà dit. Ce n'est même pas plus facile à utiliser. –

0

Vous pouvez ajouter une méthode supplémentaire. Quelque chose comme:

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; 

Je n'ai pas encore compilé/testé le code. L'idée est d'avoir une méthode de setter générale qui fonctionne avec un pointeur sur l'emplacement. Les versions spécialisées appellent simplement la méthode gerneral avec l'adresse de la variable à définir. Vous devez cependant avoir un type de méthode de définition générale par type de variable (double, entier, ...). Vous pouvez le modifier pour travailler sur Pointer et la longueur d'une variable pour travailler avec tous les types - votre décision si cela en vaut la peine.

+0

Plutôt qu'un pointeur, vous devriez utiliser un paramètre var comme dans la suggestion de Cobus Kruger. – dummzeuch

+0

c'est juste du sucre syntaxique, bien qu'il soit plus agréable à lire. –

+1

@Tobias: Non, pas seulement du sucre syntaxique. Avec le paramètre var, il est impossible de passer un pointeur NULL, donc la méthode SetValue() n'a pas à vérifier cela (BTW, votre code ne le fait pas!). Une API avec moins de risques d'abus est simplement meilleure. – mghie

7

Delphi prend en charge les propriétés indexées . Plusieurs propriétés peuvent partager un getter ou setter, différencié par un indice ordinal:

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; 

Vous pouvez combiner avec Cobus's answer pour obtenir ceci:

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

Cela pourrait vous donner des idées pour d'autres façons dont vous pouvez se référer à vos champs par index au lieu d'avoir deux champs complètement séparés pour ces valeurs connexes.

+0

+1 très intéressant! Je n'étais pas au courant de cela. – jpfollenius

+0

+1 pour la réponse que j'ai presque donné mais j'ai pensé que ce serait trop long. On dirait qu'il peut encore être décrit de manière compacte après tout :-) –

0

si les paramètres de procédure/fonction sont identiques et code entre début et de fin sont les mêmes, alors vous pouvez simplement utiliser

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

Ca y est ...

Questions connexes