C# 只允许一个 class 为 属性 调用不同的 class 的 setter (或者让 class 知道它是否存储在另一个 class)

C# Only allow one class to call a different class's setter for a property (Or have class know if it's stored in an array in another class )

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

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

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

然而,任何其他 class 也可以访问 Food.Container,如果他们写入,原始 Barrel 仍然有stores 中的此 Food 实例,但 Food.Container 将不再反映该实例。

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

我也可以做到 Food 根本不存储它的容器,只是让 Rot() 调用一个方法 bool IsStored() 来检查每个 Barrel.stores看看调用它的 Food class 的实例是否存储在里面,这样就没有双向恶意,但这对于优化来说似乎很糟糕。

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

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

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

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));

输出:

Barrel 1:Fish@Barrel 1,Chicken@Barrel 1
Barrel 1:Fish@Barrel 1
Barrel 1: