节点和处理/删除它们

时间:2017-03-15 23:11:45

标签: c# garbage-collection dispose

嗯,我已经把自己变成了一个混乱的物体,它们互相引用。显示一个最小的例子太复杂了,所以我将解释这些对象之间的关系。

我也没有寻找代码审查,而是寻找一种方法/范例,我可以使用它来允许删除而不破坏它们之间的关系行为。并保留应用程序的状态。然而,我也希望保持彼此足够分离的物体,而不需要彼此。

环境
我拥有的对象如下: Obj_A Obj_B Obj_A_FactoryObj_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_BObj_A"会发生什么?只是将它们从工厂类的列表中删除就会留下对#34;不存在的引用"对象。 (或者GC不会删除它们:它们仍然存在并被引用,而程序的其余部分认为它们被删除了。)

在RAII世界中,我只需使用Obj_B的析构函数来解析引用列表,对于{{1}列表集的父引用中的每个null }。 并且Obj_B的析构函数将转到父Obj_A并从列表中删除对该对象的引用"。

现在,如果析构函数是确定性的,那么这是有效的。在非确定性世界中,不能使用它。

糟糕的解决方案
与C#的析构函数最接近的是Dispose()。在dispose方法中,我可以做与析构函数类似的操作。然后,当我从工厂的列表中删除一个元素时,我也会在该元素上调用dispose。

然而,这给我留下了另一个问题:如果Obj_A_FactoryObj_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);
    }
}

2 个答案:

答案 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_AObj_B彼此直接引用,那么只要您的代码中某个对象直接引用它们,它们仍将驻留在内存中。换句话说,您必须使用内存分析器进行测试,以找出这些对象在垃圾回收中幸存的频率。