代码之家  ›  专栏  ›  技术社区  ›  Kenneth Cochran

如何重写计算值?

c#
  •  2
  • Kenneth Cochran  · 技术社区  · 16 年前

    所以我有以下代码片段:

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

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

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

    4 回复  |  直到 16 年前
        1
  •  2
  •   Community Mohan Dere    9 年前

    我主要是基于 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> 是 无效的 (或者更准确地说,未定义,但结果是相同的)。

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

    我走这条路是因为这个类中的属性需要填充一个可序列化的对象,该对象最终以XML形式传输。XML的模式包括定义为整数、小数和字符串混合的数字字段。

    然后使用可重写类,如下所示:

    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 是非静态方法,因此不能在字段初始值设定项中使用。因为类中的所有属性都是非静态的,所以我必须初始化构造函数中的所有支持字段。

        2
  •  1
  •   Vlad    16 年前

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

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

    优点是整个逻辑隐藏在类中。

        3
  •  1
  •   JaredPar    16 年前

    您可以通过定义一个方便的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); }
    }
    
        4
  •  0
  •   D'Arcy Rittich    16 年前

    这在我看来是合理的。我唯一能做的改变就是缓存 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;
        }
    }
    

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