嗯,我已经把自己变成了一个混乱的物体,它们互相引用。显示一个最小的例子太复杂了,所以我将解释这些对象之间的关系。
我也没有寻找代码审查,而是寻找一种方法/范例,我可以使用它来允许删除而不破坏它们之间的关系行为。并保留应用程序的状态。然而,我也希望保持彼此足够分离的物体,而不需要彼此。
环境
我拥有的对象如下:
Obj_A
Obj_B
Obj_A_Factory
和Obj_B_Factory
Obj_A_Factory
只是Obj_A
的工厂。它还跟踪此类的所有实例。并保留每个Obj_A
是"唯一"的事实。
Obj_B_Factory
的类似故事 - 它是Obj_B
的工厂,它保留了一个列表(实际上这些是字典,但除了这一点之外)。
现在Obj_A
包含一个列表,其中包含对(多个)Obj_B
的引用。如果Obj_B
没有添加Obj_A
,则此列表可以为空。
Obj_B
作为回应保留了对父母的引用" Obj_A
也添加了null
,并注明Obj_A
是一个完整的有效值:表示它尚未添加到任何值。
这非常类似于"树"但不同之处在于节点不拥有他们的孩子,而是他们引用它并且所有权由外部管理者完成。
问题
如果我希望删除Obj_B
或Obj_A
"会发生什么?只是将它们从工厂类的列表中删除就会留下对#34;不存在的引用"对象。 (或者GC不会删除它们:它们仍然存在并被引用,而程序的其余部分认为它们被删除了。)
在RAII世界中,我只需使用Obj_B
的析构函数来解析引用列表,对于{{1}列表集的父引用中的每个null
}。
并且Obj_B
的析构函数将转到父Obj_A
并从列表中删除对该对象的引用"。
现在,如果析构函数是确定性的,那么这是有效的。在非确定性世界中,不能使用它。
糟糕的解决方案
与C#的析构函数最接近的是Dispose()
。在dispose方法中,我可以做与析构函数类似的操作。然后,当我从工厂的列表中删除一个元素时,我也会在该元素上调用dispose。
然而,这给我留下了另一个问题:如果Obj_A_Factory
或Obj_B_Factory
被删除怎么办? (而其他人则不是)。然后是列表"包含/拥有Obj_A
"会被删除;预期的结果是所有提到Obj_A
的所有内容都将被开放用于垃圾收集,并且不再有效被提及。
因此,当我希望释放Dispose()
时,我必须在Obj_A
拥有的所有Obj_A_Factory
上致电Obj_A_Factory
。换句话说:它还需要实现Dispose()
。
然后会升级,因为包含Obj_A_Factory
...
这是正确的方法吗?或者我应该使用其他一些构造来管理它吗?有没有办法可以"密封" Dispose()
的级联,以便我可以抽象出这个内存管理(例外......)混乱?
最小例子
这是我可以创建的最小示例,之前任何管理选项。我希望删除工作:目前它会破坏"状态"。请注意,它没有"做"任何有卡片表等的东西,它只是显示了班级之间的关系
另请注意,我没有任何代码显示"删除工厂" - 但我希望也支持这一点,我不希望工厂像单身人士那样行事。
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);
}
}
答案 0 :(得分:1)
这似乎很简单,所以也许我错过了什么。
当B工厂移除B对象时,它告诉关联的A B被移除,因此A可以移除其对B的引用。
删除A对象后,删除其所有B引用,并告诉B工厂正在删除引用。
不要乱用Dispose。对于粗心的C ++开发人员来说,这是一个陷入C#的陷阱。
答案 1 :(得分:1)
听起来您正在为节点寻找WeakReference<T>
。 WeakReference
保存对托管对象的引用,同时仍允许它由垃圾回收器回收。如果您的应用程序完成了对该对象的所有引用,但唯一的引用是工厂中的引用,则垃圾收集器将收集该实例,TryGetTarget(out T)
方法返回false,引用为null
WeakReference<T>
的缺点是它是另一个占用内存的对象。但是,它是为这种情况而设计的。
我会提醒您,如果您的Obj_A
和Obj_B
彼此直接引用,那么只要您的代码中某个对象直接引用它们,它们仍将驻留在内存中。换句话说,您必须使用内存分析器进行测试,以找出这些对象在垃圾回收中幸存的频率。