如何覆盖计算值?

时间:2010-02-26 16:06:37

标签: c#

所以我有以下代码片段:

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

所以基本上我试图实现的行为是,如果一个字段留空或被赋值等于计算出的值,则使用计算值,否则存储指定的值。

我有很多字段需要支持这样的覆盖。这是实现这一目标的最佳方式吗?如果不是你会建议什么?

4 个答案:

答案 0 :(得分:2)

我主要根据Vlad的建议对此进行了研究。事实证明,你可以使用一个通用类来抽象它。结果如下:

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; }
    }
}

您需要传入一个比较委托,因为在您将其设置为子类之前,该类型是未知的。所以一个简单的==不会削减它。我使用了一个简单的路由并使用了一个Func委托,但是如果由于某种原因它必须适应.NET 2.0,那么可以用普通委托替换它。

您会注意到我使用的是default(T)而不是null。这是有效的,因为Nullable<T>的默认值是null(或者更确切地说,未定义,但它的结果相同)。

这并不妨碍您尝试为非可空类型声明Overridable<T>。你最终得到的不是通过运行时错误,但它没有那么有用。尝试将Overridable<decimal>.Value设置为null会导致编译错误。将其设置为default(decimal)会使其恢复为计算值。

我选择了这条路由,因为我正在使用它的类中的属性需要填充一个最终作为xml传输的可序列化对象。 xml的模式包括定义为整数,小数和字符串混合的数字字段。

然后使用Overriddable类,如下所示:

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

我遇到的唯一问题是CalculateExcessWages是非静态方法,因此无法在字段初始值设定项中使用。由于我的类中的所有属性都是非静态的,因此我必须初始化构造函数中的所有支持字段。

答案 1 :(得分:1)

您可以为此创建一个类包装器。

class OverridableValue<T>
{
    public OverridableValue<T>(Func<T> calculator)
    {
        _calculator = calculator;
    }
    private Nullable<T> _t;
    private Func<T> _calculator;
    public T Get()
    {
        return return _t ?? _calculator();
    }
    public void Set(T value)
    {
        _t = (value != _calculator()) ? value : null;
    }
}

它在语法上并不那么甜美,但至少可以省去一些按键。

现在您可以像这样使用它:

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

优点是整个逻辑隐藏在课堂上。

答案 2 :(得分:1)

你可以通过定义一个方便的set / get helper方法

来做到这一点
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); }
}

答案 3 :(得分:0)

这看起来很合理。我可能做的唯一改变是缓存CalculateExcessWages(),如果它很昂贵,可以缓存:

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;
    }
}

但是,这比你的代码更多,我认为你正在寻求简化。