如何基于彼此更改两个可观察属性,但防止死锁?
为了这个例子,我保持非常简单:
我正在数两件事,(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;
}
}
如何防止死锁,例如仅在用户调用时更改属性一次?
您可以实现一个标志,以便只有您正在设置的属性才会触发另一个属性的设置。您设置部分,它会更新部分和金额,但金额不会触发部分更新。
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;
}
}
}
}
然后您可以调用设置器,而不必担心无限循环:
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
创建两个方法来更新属性
void SetAmount(int amount)
{
this.Amount = amount;
Sections = Convert.ToInt32(Math.Round(Amount / 3));
}
void SetSections(int sections)
{
this.Sections = sections;
Amount = Sections * 3;
}
这样属性设置器就不会递归地互相调用
与其拥有两个单独的值并尝试维护它们之间的数据完整性,不如“仅拥有一个存储值”并让其中一个属性完全是计算值。 这不仅意味着不需要递归来解决,而且对象也不可能处于“无效”状态。
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;
}