. net逻辑中的setter和后备字段

本文关键字:字段 setter net | 更新日期: 2023-09-27 18:10:43

我有两个属性,其中后一个(CalulcatedValue)必须在第一个(FXRate)更改时更新。

我可以通过使CalulcatedValue只读并从FXRate setter调用OnPropertyChanged("CalculatedValue")来实现这一点。

public double FXRate {
    get { return _fXRate; }
    set {
        Set(_fXRate, value, "FXRate");
        OnPropertyChanged("CaluclatedValue");
    }
}
public float CalculatedValue  
{
    get { return FXRate * SomeOtherValue;}
}

但我知道CalulcatedValue属性将被调用很多(在几个LINQ查询等中使用)。我在考虑getter应该非常快地返回值,并计划为它添加一个后备字段。

public double FXRate 
{
    get { return _fXRate;}
    set {
        Set(_fXRate, value, "FXRate");
        CalculatedValue = 0; //how to set CalculatedValue since it's setter is calculating it with its logic, i.e. the value parameter is not needed ?
    }
}
private double _fXRate;
public float CalculatedValue  
{
    get { return _calculatedValue; }
    set {
        __calculatedValue = FXRate * SomeOtherValue); //consider this line takes some effort to be calculated -> instead of SomeOtherValue there might be pretty complex logic here to get the result
        OnPropertyChanged("CalculatedValue");
    }
}
private float _calculatedValue;

CalulcatedValue现在应该如何设置?它不需要传递任何值形参,因为逻辑在setter内部。

CalulcatedValue = 0

. net逻辑中的setter和后备字段

如果需要INotifyPropertyChanged通知,则将其添加到FXRate属性(和/或任何可能改变CalculatedValue值的属性)中,并在那里进行计算:

private double _fXRate;
private float _calculatedValue;
public double FXRate 
{
  get { return _fXRate;}
  set {
      Set(_fXRate, value, "FXRate");
      _calculatedValue = _fxRate * SomeOtherValue;
      // this will update any control depending on the `CalculatedValue` notification
      OnPropertyChanged("CalculatedValue");
  }
}
public float CalculatedValue  
{
  get { _calculatedValue; }
}

或者,如果您希望将计算推迟到第一次读取(因为您将在实际读取CalculatedValue之前多次更新FxRate或其他内容),您可以这样做:

private double _fXRate;
private float _calculatedValue;
private bool _calculatedValueIsDirty = true;
public double FXRate 
{
  get { return _fXRate;}
  set {
      Set(_fXRate, value, "FXRate");
      _calculatedValueIsDirty = true;
      OnPropertyChanged("CalculatedValue");
  }
}
public float CalculatedValue  
{
  get { 
      if(_calculatedValueIsDirty) {
        _calculatedValue = _fxRate * SomeOtherValue;
        _calculatedValueIsDirty = false;
      }
      return _calculatedValue;
  }
}

CalculatedValue的后续读取将很快返回,除非预计算的值再次变脏(通过更改FXRate)

PS:如果有类似多线程的东西,在必要的地方应用锁:-)

因为CalculatedValue是计算其他属性的结果,所以它不应该有setter。如果出于性能原因不想在get上计算,可以像这样预先计算:

public double FXRate 
{
   get { return _fXRate;}
   set 
   {
        Set(_fXRate, value, "FXRate");
        CalculateStuff();
   }
}
private double _fXRate;
public float CalculatedValue  
{
    get { return _calculatedValue; }
}
private void CalculateStuff()
{
    // This calculation is private to the class so I see no reason to not use the fields..
    _calculatedValue = _fXRate * SomeOtherValue; //consider this line takes some effort to be calculated -> instead of SomeOtherValue there might be pretty complex logic here to get the result
    OnPropertyChanged("CalculatedValue");
}
private float _calculatedValue;

根本不需要setter。你只需要一个getter属性:

public float CalculatedValue  
{
    get { return FXRate * SomeOtherValue; }
}

如果你使用c# 6:

public float CalculatedValue => FXRate * SomeOtherValue;
编辑:

由于计算的值很耗时,也许getter应该有更多的"beef",因为你有一个扩展的算法来决定是否应该计算属性。

private float calculatedValue;
public float CalculatedValue  
{
    get 
    { 
        if (ShouldRecalculate())
        {
            calculatedValue = DoHeavyCalculation();
        }
        return calculatedValue;
    }
}

好的,所以您的示例计算是一个非常便宜的计算,如果放入缓冲变量中,将不会产生明显的改进。为了给出这个答案,我假设您实际进行的计算要复杂得多,也要昂贵得多。

现在对于属性来说,确保它们在执行时花费相同的时间并且没有很多不相关的副作用总是一件好事。这样你的代码就显得简单了。

我认为你已经很接近你真正想要的了。无论如何,我将避免隐藏在您的财产设置中的任何不可读的黑客。另外,我强烈建议在这里使用私有setter,以避免外部任何人扰乱您的值。

我会这样写:

public double FXRate 
{
    get { return _fXRate;}
    set {
        Set(_fXRate, value, "FXRate");
        CalculatedValue = value * SomeOtherValue;
        /* The calculation is completely done here. Using the value parameter because 
         * it is local and has slightly less overhead then accessing the class variable
         * or the property we are just setting. It is in general a good idea to avoid
         * reading the property in the setter. */
    }
}
private double _fXRate;
public float CalculatedValue  
{
    get { return _calculatedValue; }
    private set {
        Set(_calculatedValue, value, "CalculatedValue");
    }
}
private float _calculatedValue;

这样你就有了一个非常干净的属性模型,计算发生在相关更改实际完成的地方。此时,您还可以检查值是否实际更改,并跳过更新昂贵的计算值,以防不需要。