C# 只允许一个类为一个属性调用另一个类的 setter或者让类知道它是否存储在另一个类的数组中

问题描述

假设我有两个类,例如 FoodBarrelBarrel 包含一组食物 private Food[] stores 以跟踪其中存储的内容Barrel 还包含访问和修改 stores方法,例如 public void Insert(Food)public void Remove(Food)

所以 Barrel 可以存储 Food。但我也希望 Food 知道它的存储位置,如果有的话。例如,我想要一个方法 Food.Rot(),它只会在没有储存的情况下腐烂食物。

我目前的解决方案是在 public Barrel Container { get; set; } 中有一个属性 Food 来跟踪它存储在其中的 Barrel 实例,并将 Barrel.Insert(Food toAdd) 设置为 toAdd.Container本身和 Barrel.Remove(Food toRemove)toRemove.Container 设置为 null在这种情况下 Food.Rot() 实际上会腐烂食物)。

然而,任何其他类也可以访问 Food.Container,如果他们写入它,原来的 Barrel 仍然会在 {Food 中拥有这个 stores 实例{1}},但 Food.Container 将不再反映这一点。

我可以让 set 方法确保当 Food.Container 改变时,它首先调用 Container.Remove(this),但我真的不希望除了 Barrel 之外的任何东西能够设置 {{1 }} 首先。

我也可以让 Food.Container 根本不存储它的容器,只需让 Food 调用方法 Rot() 来检查每个 bool Isstored() 以查看是否调用它的 Barrel.stores 类的实例存储在内部,这样就没有双向错误,但这对于优化来说似乎很糟糕。

感觉就像我在这个设计的某个地方违反了 OOP,我希望理想情况下需要重新设计整个情况。我也非常愿意接受此类反馈。

解决方法

食物添加到后,您必须修改食物属性。此外,当设置了 Barrel 属性时,如果不存在,您必须将食物添加到 Barrel 中。检查是否存在很重要,否则这两个将相互调用导致 StackOverflow 异常。

您在删除时也必须重复相同的方法。这是您需要的示例:

grecaptcha.enterprise.ready(function() {
                grecaptcha.enterprise.execute(scoreKey,{action: action}).then(function(token) {
                    $('#g-recaptcha-response').val(token);
                    submitForm();
                });
              });

测试代码:

public class Food
{
    public string Name { get; set; }
    private Barrel _barrel;
    public Barrel Barrel
    {
        get
        {
            return _barrel;
        }
        set
        {
            if (_barrel != null && _barrel != value)
            {
                _barrel.Remove(this);
            }
            if (value != null)
            {
                value.Insert(this);
            }
            _barrel = value;
        }
    }
    public override string ToString()
    {
        return Name+"@"+ Barrel??"";
    }
}
public class Barrel
{
    public string Name { get; set; }
    public List<Food> Stores { get; set; } = new List<Food>();

    public void Insert(Food food)
    {
        if (!Stores.Contains(food))
        {
            Stores.Add(food);
            food.Barrel = this;
        }
    }
    public void Remove(Food food)
    {
        if (Stores.Contains(food))
        {
            Stores.Remove(food);
            food.Barrel = null;
        }
    }
    public override string ToString()
    {
        return Name;
    }
}

输出:

    Barrel b0 = new Barrel() { Name = "Barrel 0" };
    Barrel b1 = new Barrel() { Name = "Barrel 1" };
    //Use insert method.
    Food fish = new Food() { Name = "Fish" };

    //I will insert fish to b0 first.
    b0.Insert(fish);

    b1.Insert(fish);
    //Assign barrel directly
    Food chicken = new Food() { Barrel = b0,Name = "Chicken" };
    //then change barrel
    chicken.Barrel = b1;

    Debug.WriteLine("Barrel 1:"+ string.Join(",",b1.Stores));

    //Modify Barrel attribute
    chicken.Barrel = null;
    Debug.WriteLine("Barrel 1:" + string.Join(",b1.Stores));

    //Remove from stores
    b1.Remove(fish);
    Debug.WriteLine("Barrel 1:" + string.Join(",b1.Stores));