2010-02-26 6 views
2

Je l'extrait de code de code suivant:Comment remplacer une valeur calculée?

private Nullable<decimal> _excessWages; 
public decimal ExcessWages 
{ 
    get 
    { 
     return _excessWages ?? CalculateExcessWages(); 
    } 
    set 
    { 
     if (value != CalculateExcessWages()) 
      _excessWages = value; 
     else 
      _excessWages = null; 
    } 
} 

Donc, fondamentalement le comportement que je suis en train de mettre en œuvre est si un champ est vide ou est attribué une valeur égale à celle calculée utiliser la valeur calculée, sinon stocker la valeur affectée.

J'ai beaucoup de champs qui ont besoin de prendre en charge le remplacement comme ceci. Est-ce la meilleure façon d'accomplir cela? Si non, que suggérez-vous?

+0

Qu'est-ce que vous demandez réellement à propos de; Je vois que la propriété est déclarée comme "virtuelle", mais à partir du texte, je comprends que vous demandez plus sur la construction du code à l'intérieur du setter? –

+0

Désolé à ce sujet. J'ai enlevé le virtuel. Ce n'est pas ce qui m'intéresse. Je suis préoccupé par «substituable» du point de vue de l'utilisateur final. –

Répondre

2

je travaillais sur ce un peu basé principalement sur la suggestion de Vlad. Il s'avère que pouvez utiliser une seule classe générique pour l'abstraire. Voici le résultat final:

public class Overridable<T> 
{ 
    private Func<T> _calculate; 
    private readonly Func<T, T, bool> _compare; 
    protected T _t; 

    public Overridable(Func<T> calculate, Func<T, T, bool> compare) 
    { 
     _calculate = calculate; 
     _compare = compare; 
    } 

    public T Value 
    { 
     get { return _compare(_t, default(T)) ? _calculate() : _t; } 
     set { _t = _compare(value, _calculate()) ? default(T) : value; } 
    } 
} 

Vous devez passer un délégué de comparer parce que le type ne sait pas jusqu'à ce que vous définissez dans une sous-classe. Donc, un simple == ne va pas le couper. Je suis allé le chemin facile et ai utilisé un délégué de Func mais ceci pourrait être remplacé par un délégué normal s'il devait être adapté pour .NET 2.0 pour une certaine raison.

Vous remarquerez que j'utilise default(T) au lieu de null. Cela fonctionne parce que la valeur par défaut pour un Nullable<T> est null (ou plus précisément, indéfini mais cela revient à être le même).

Cela ne vous empêche pas d'essayer de déclarer un Overridable<T> pour un type non-nullable. Ce que vous obtiendriez ne se traduira pas par des erreurs de temps d'exécution, mais ce n'est pas aussi utile. Essayer de définir un Overridable<decimal>.Value à null vous obtiendrez une erreur de compilation. Lorsque vous le définissez sur default(decimal), il revient au calcul de la valeur.

Je suis allé cette route parce que les propriétés de cette classe que je l'utilise dans le besoin de remplir un objet sérialisable qui est finalement transmis en tant que xml. Le schéma pour le xml comprend des champs numériques définis comme un mélange d'entiers, de décimales et de chaînes.

Vous utilisez alors la classe overriddable comme ceci:

private Overridable<decimal?> _excessWages = 
    new Overridable<decimal?>(CalculateExcessWages, (x,y) => x == y); 
public virtual decimal? ExcessWages 
{ 
    get 
    { 
     return _excessWages.Value; 
    } 
    set 
    { 
     _excessWages.Value = value; 
    } 
} 

Le seul problème que je suis tombé sur c'était que CalculateExcessWages est une méthode non statique ne peut donc pas être utilisé dans un initialiseur sur le terrain. Comme toutes les propriétés de ma classe ne sont pas statiques, j'ai dû initialiser tous les champs de sauvegarde dans le constructeur.

0

Cela semble raisonnable à mes yeux. Le seul changement que je pourrais faire est de mettre en cache CalculateExcessWages(), si elle est coûteuse à faire, et ok à cache:

private Nullable<decimal> _excessWages; 
private Nullable<decimal> _excessWagesCalculated; 
public virtual decimal ExcessWages 
{ 
    get 
    { 
     if (_excessWagesCalculated == null) 
      _excessWagesCalculated = CalculateExcessWages(); 
     return _excessWages ?? _excessWagesCalculated; 
    } 
    set 
    { 
     if (_excessWagesCalculated == null) 
      _excessWagesCalculated = CalculateExcessWages(); 
     if (value != _excessWagesCalculated) 
      _excessWages = value; 
     else 
      _excessWages = null; 
    } 
} 

Mais, cela est plus de code que le vôtre, et je pense que vous cherchez à simplifier.

+0

Hmm, intéressant. Cela peut ou peut ne pas être un problème. Beaucoup de ces propriétés remplaçables sont calculées à partir d'autres propriétés remplaçables, qui sont calculées à partir d'autres propriétés, etc. Ainsi, une propriété déclencherait une chaîne de calculs chaque fois qu'elle est accédée ou modifiée. Cela se produit lors du chargement d'un objet à partir d'une base de données, l'utilisateur remplace l'une des propriétés ou l'utilisateur imprime un rapport ou sérialise l'objet. Quelque chose à quoi penser. –

1

Vous pouvez créer un wrapper de classe pour cela. Ce n'est pas tellement syntactiquement doux, mais au moins enregistre quelques frappes.

Maintenant, vous pouvez l'utiliser comme ceci:

class Foo 
{ 
    OverridableValue<decimal> _excessWages = 
      new OverridableValue<decimal>(CalculateExcessWages); 
    public decimal ExcessWages 
    { 
     get { return _excessWages.Get(); } 
     set { _excessWages.Set(value); } 
    } 
    ... 
} 

L'avantage est que toute la logique est cachée à la classe.

+0

Toutes les propriétés remplaçables ne sont pas des types de valeur, donc j'ai besoin de le modifier afin que 't' puisse être affecté à n'importe quel type, pas seulement à nullable. –

+0

vous auriez besoin essentiellement de 2 génériques: un avec 'where T: class', et un pour les types de valeur. – Vlad

1

Vous pouvez le faire en définissant un ensemble pratique/get méthode d'assistance

private static T GetUtil<T>(ref Nullable<T> value, Func<T> calc) { 
    return value ?? calc(); 
} 

private static void SetUtil<T>(ref Nullable<T> value, T newValue, Func<T> calc) { 
    if (newValue != calc()) { 
    value = newValue 
    } else { 
    value = null; 
    } 
} 

private Nullable<decimal> _excessWages; 
public decimal ExcessWages 
{ 
    get { return GetUtil(ref _excessWages, CalculateExcessWages); } 
    set { SetUtil(ref _excessWages, value CalculateExcessWages); } 
} 
Questions connexes