代码之家  ›  专栏  ›  技术社区  ›  Al John

防止两个属性相互依赖的死锁

  •  1
  • Al John  · 技术社区  · 9 月前

    如何基于彼此更改两个可观察属性,但防止死锁?

    为了这个例子,我保持简单:
    我在数两件事,(1)物品和(2)这些物品的容器。每个容器可以容纳3个物品。用户可以更改物品的数量和容器的数量。更改一个会自动更改另一个。

    正如你所想象的,这会导致完全的僵局。

    显示值:

    <Entry Text="{Binding Amount}"/>
    <Entry Text="{Binding Sections}"/>
    

    设置值:

    private int amount;
    public int Amount
    {
        get => amount;
        set
        {
            SetProperty(ref amount, value);
            Sections = Convert.ToInt32(Math.Round(Amount / 3));
        }
    }
    
    private int sections;
    public int Sections
    {
        get => sections;
        set
        {
            SetProperty(ref sections, value);
            Amount = Sections * 3;
        }
    }
    

    如何防止死锁,例如在用户调用时只更改一次属性?

    3 回复  |  直到 9 月前
        1
  •  2
  •   JimmyV    9 月前

    您可以实现一个标志,以便只有您正在设置的属性触发另一个属性的设置。您设置了部分,它会更新部分和金额,但金额不会触发部分更新。

    public class TestClass
    {
        private bool _updating = false;
    
        private int _amount;
        public int Amount
        {
            get => _amount;
            set
            {
                _amount = value;
                if (!_updating)
                {
                    _updating = true;                 
                    Sections = Convert.ToInt32(Math.Round(Convert.ToDouble(Amount / 3)));
                    _updating = false;
                }
            }
        }
    
        private int _sections;
        public int Sections
        {
            get => _sections;
            set
            {
                _sections = value;
                if (!_updating)
                {
                    _updating = true;
                    Amount = _sections * 3;
                    _updating = false;
                }
            }
        }
    }
    

    然后,您可以调用setter,而不必担心无限循环:

    static void Main()
    {
        var tc = new TestClass();
    
        Console.WriteLine("Set sections = 5");
    
        tc.Sections = 5;
    
        Console.WriteLine($"Sections: {tc.Sections}; Amount: {tc.Amount}");
    
        Console.WriteLine("Set amount = 10");
    
        tc.Amount = 10;
    
        Console.WriteLine($"Sections: {tc.Sections}; Amount: {tc.Amount}");
    }
    

    产生以下结果:

    设置节数=5

    第5节; 金额:15

    设定金额=10

    第3节; 金额:10

        2
  •  1
  •   Jason    9 月前

    创建两种方法来更新属性

    void SetAmount(int amount)
    {
      this.Amount = amount;
      Sections = Convert.ToInt32(Math.Round(Amount / 3));
    }
    
    void SetSections(int sections)
    {
      this.Sections = sections;
      Amount = Sections * 3;
    }
    

    这样,属性设置器就不会递归地相互调用

        3
  •  1
  •   Servy    9 月前

    与其有两个单独的值并试图保持它们之间的数据完整性,不如 只有一个存储值 并且让其中一个属性是一个完全计算的值。这不仅意味着没有递归需要解决,而且对象不可能有“无效”的状态。

    private int amount;
    public int Amount
    {
        get => amount;
        set
        {
            SetProperty(ref amount, value);
        }
    }
    
    public int Sections
    {
        get => (int)Math.Round(Amount / 3);
        set => Amount = Sections * 3;
    }
    
    推荐文章