Justin Iaconis
Justin Iaconis

Reputation: 43

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 )

So let's say I have two classes, Food and Barrel for example. Barrel contains an array of food private Food[] stores to track what's stored in it. Barrel also contains methods to access and modify stores, such as public void Insert(Food) and public void Remove(Food).

So Barrel can store Food. But I also want Food to know where it's stored in, if anywhere. For example, I want to have a method Food.Rot(), which only rots food if it's not stored.

My current solution is to have a property public Barrel Container { get; set; } in Food that tracks the instance of Barrel it's stored in, and have Barrel.Insert(Food toAdd) set toAdd.Container to itself and Barrel.Remove(Food toRemove) set toRemove.Container to null (under which condition Food.Rot() would actually rot the food).

However, any other class can also access Food.Container, and if they write to it, the original Barrel will still have this Food instance in stores, but Food.Container will no longer reflect that.

I can make the set method ensure that when Food.Container changes, it first calls Container.Remove(this), but I really don't want anything besides Barrel to be able to set Food.Container in the first place.

I could also make it so Food doesn't store its container at all and simply have Rot() call a method bool IsStored() to check every single Barrel.stores to see if the instance of the Food class that called it was stored inside, that way there's no two-way malarkey, but that seems just terrible for optimization.

It feels like somewhere along this design I've violated OOP and I expect that ideally this whole situation needs to be redesigned. I'm more than open to such feedback as well.

Upvotes: 0

Views: 1733

Answers (1)

aliassce
aliassce

Reputation: 1197

When Food added into a Barrel you must modify the Barrel attribute of the Food. Also when the Barrel attribute is set you must add the food into the Barrel if not exists. Checking exists is important because otherwise, these two will call each other to cause a StackOverflow Exception.

You must also repeat the same approach when removing. Here is the sample you need:

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

Test code:

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

Output:

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

Upvotes: 1

Related Questions