【问题标题】:Calculated read-only properties计算的只读属性
【发布时间】:2020-11-09 18:29:54
【问题描述】:

我正在尝试在对象上实现属性,这些属性的值派生自其他对象的属性值。例如,名为 Student 的类中名为 TotalPoints 的属性在 get{} 中有以下代码,

foreach(var subject in Subjects)
{
  _totalPoints += subject.Points;
}

return _totalPoints;

这适用于简单的情况。但是,当涉及更多级别时,例如,Subject.Points 实际上是指其他东西。计算会慢一些。我想弄清楚的是:

  • 如果有什么方法可以检测Points的值是否有任何变化,如果没有,我不想重新计算_totalPoints;

  • 有没有其他方法可以实现这一点?我也在想是否可以“通知” TotalPoints 其中一个 Subject.Points 的值已经改变,所以 TotalPoints 需要重新计算。

感谢任何人有任何想法。谢谢。

【问题讨论】:

  • 执行此操作的经典方法是让您的类型实现 INotifyPropertyChanged 接口,并且持有它们的类型应订阅其每个属性的 PropertyChanged 事件,计算 @更改Points 属性时的 987654324@ 值 - 但是,这会增加代码的复杂性,除非计算速度真的很慢(也就是说,人类很明显)我会三思而后行,然后再更喜欢你现在拥有的更简单的代码.

标签: c# .net-core


【解决方案1】:

你描述的是一个事件驱动的系统(见dot net events)。这是一个简单的例子:

public class PointsProvider
{
    public event Action<int> OnPointsChanged;
    private int points;
    public int Points
    {
        get => points;
        set
        {
            if (points == value)
                return;
            int oldValue = points;
            points = value;
            OnPointsChanged?.Invoke(points - oldValue);
        }
    }
}

public class TotalPointsProvider
{
    private PointsProvider providerA, providerB;

    private int totalPoints;
    public int TotalPoints => totalPoints;

    public TotalPointsProvider()
    {
        providerA = new PointsProvider();
        providerA.OnPointsChanged += UpdateTotalPoints;
        providerB = new PointsProvider();
        providerB.OnPointsChanged += UpdateTotalPoints;

        totalPoints = providerA.Points + providerB.Points;
    }

    private void UpdateTotalPoints(int delta)
    {
        totalPoints += delta;
    }
}

TotalPointsProvider 收到通知,一旦其中一个 PointsProviders 发生更改并更新它自己的值。

如果您的子系统 (PointsProvider) 有很多更改但不经常请求结果,您可能想要构建一个惰性系统:

public class PointsProvider
{
    public event Action OnPointsChanged;
    private int points;
    public int Points
    {
        get => points;
        set
        {
            if (points == value)
                return;
            points = value;
            OnPointsChanged?.Invoke();
        }
    }
}

public class TotalPointsProvider
{
    private PointsProvider providerA, providerB;

    private bool dirty = true;

    private int totalPoints;
    public int TotalPoints
    {
        get
        {
            if (dirty)
            {
                totalPoints = providerA.Points + providerB.Points;
                dirty = false;
            }
            return totalPoints;
        }
    }

    public TotalPointsProvider()
    {
        providerA = new PointsProvider();
        providerA.OnPointsChanged += RecalculationNecessary;
        providerB = new PointsProvider();
        providerB.OnPointsChanged += RecalculationNecessary;
    }

    private void RecalculationNecessary()
    {
        dirty = true;
    }
}

【讨论】:

  • 如果您使用自己的实现而不是INotifyPropertChanged,您不妨将旧值和新值之间的差异发送给侦听器,这样就不必重新计算整个事情了计算将只是添加该差异(注意,差异可能是正数或负数)。否则,您只是将瓶颈从 TotalPoints 属性的 getter 移动到 setter...
  • @ZoharPeled 我知道。我只是想勾勒出前进的方向。根据具体情况,直接值更新甚至可能不是最高效的解决方案。
  • 嗯,懒人版更好,所以+1
  • @Thomas 感谢您的回复。您提供的第二个示例确实给了我一些想法。我认为这似乎是最简单的方法。
【解决方案2】:
get
    {
        return _totalPoints;
    }
set
    {
        foreach(var subject in Subjects)
         {
        _totalPoints += subject.Points;
         }
    }

【讨论】:

  • 这里不赞成纯代码答案。尝试添加一些解释!
猜你喜欢
  • 2014-07-24
  • 2014-07-27
  • 1970-01-01
  • 2011-03-02
  • 1970-01-01
  • 1970-01-01
  • 2014-01-21
  • 1970-01-01
相关资源
最近更新 更多