Well I've gotten myself into a messy complex of objects that refer to each other. It is too complex to show a minimal example so I'll explain the relations between those objects.
I'm also not looking for code review, rather for a method/paradigm I can use to allow deletion without breaking the relational behaviour between those. And preserve the application's state. Yet I also wish to keep the objects separated enough from each other that they work without needing each other.
Environment
The objects I have are the following:
Obj_A
Obj_B
Obj_A_Factory
and Obj_B_Factory
Obj_A_Factory
is simply a factory for Obj_A
. It also keeps track of all instances of this class. And preserves the fact that each Obj_A
is "unique".
Similar story for Obj_B_Factory
- it's a factory for Obj_B
that keeps a list (actually those are dictionaries but that's besides the point).
Now Obj_A
contains a list with references to (a number of) Obj_B
. This list can be empty if no Obj_B
is added to Obj_A
.
Obj_B
in response keeps a reference to the "parent" Obj_A
it is added too, with the note that null
is a complete valid value here: indicating it hasn't been added to any.
This is very similar to a "tree", however with the difference that the nodes do NOT own their childs, rather they refer to it and the ownership is done by an external manager.
Problem
What happens if I wish to "remove an Obj_A
or Obj_B
". Just removing them from the list in the factory classes would leave references to "inexistant" objects. (Or as GC won't remove them: they would still exist and be refered to, while the rest of the program thinks they are removed).
In a RAII world I would simply use the destructor of Obj_A
to "iterate through the list of references, for each Obj_B
in the list set parent-reference to null
.
And the destructor for Obj_B
would "go to the parent Obj_A
and remove the reference to this object from the list".
Now this works if a destructor is deterministic. In a non deterministic world this can't be used.
Bad solution
The closest thing to destructors for C# is Dispose()
. In the dispose method I can do a similar thing as the destructors. Then when I remove an element from the list in the factories I would also call dispose on that element.
This, however, leaves me with another problem: What if Obj_A_Factory
or Obj_B_Factory
get deleted? (while the others aren't). Then again the list "containing/owning Obj_A
" would get deleted; The expected result being that all refered to Obj_A
's would be open for garbage collection, and are no longer valid to be refered to.
So I would have to call Dispose()
on all Obj_A
owned by Obj_A_Factory
, when I wish to free Obj_A_Factory
. In other words: it also needs to implement Dispose()
.
This would then escalate, because what about the class that contains the Obj_A_Factory
...
Is this the right way to go about it? Or is there some other construct I should use to manage this? Is there a way I can "seal" the cascading of Dispose()
so that I can abstract away this memory management (exceptions..) mess?
minimal example
This is a minimal example I can create, before any of the management options. I wish to get the removing working: currently it would break the "state". Notice it doesn't "do" anything with the cardlist etc yet, it just shows the relations between the classes.
Also notice I do not have any code that shows "deletion of the factories" - however I wish to also support this, I do not wish the factories to behave as singletons.
class Obj_A_Deck
{
public string Name { get; }
public IList<Obj_B_Card> cardList { get; };
public Obj_A_Deck(string name) {
Name = name;
}
}
class Obj_B_Card
{
public string Name { get; }
public Obj_A_Deck MyDeck { get; private set; }
public Obj_B_Card(string name) {
Name = name;
MyDeck = null;
}
public void AttachToDeck(Obj_A_Deck deck) {
if (MyDeck == deck) return;
MyDeck = deck;
MyDeck?.cardList.Add(this);
}
}
class Obj_A_Factory_DeckList
{
private readonly Dictionary<string, Obj_A_Deck> _deckList = new Dictionary<string, Obj_A_Deck>();
Obj_A_Deck CreateDeck(string name) {
var d = new Obj_A_Deck(name);
_deckList.Add(d.Name, d); //exception guard against duplicates
return d;
}
void RemoveDeck(string name) {
_deckList.Remove(name);
}
}
class obj_B_Factory_Inventory
{
private readonly Dictionary<string, Obj_B_Card> _cardList = new Dictionary<string, Obj_B_Card>();
Obj_B_Card CreateCard(string name) {
var c = new Obj_B_Card(name);
_cardList.Add(c.Name, c); //exception guard against duplicates
return c;
}
void RemoveCard(string name) {
_cardList.Remove(name);
}
}