在C#和.Net中执行父子关系

问题描述 投票:7回答:5

让我们学习以下两个课程:

public class CollectionOfChildren
{
    public Child this[int index] { get; }
    public void Add(Child c);
}

public class Child
{
    public CollectionOfChildren Parent { get; }
}

Child的Parent属性应始终返回Child所在的CollectionOfChildren;如果孩子不在此类集合中,则返回null。在这两个类之间,该不变性应得到维护,并且该类的使用者不应被破坏(很容易)。

您如何实现这种关系? CollectionOfChildren无法设置Child的任何私有成员,那么应该如何通知Child它已添加到集合中?(如果孩子已经是集合的一部分,则抛出异常是可以接受的。)


internal关键字已被提及。我现在正在编写WinForms应用程序,因此所有内容都在同一程序集中,这与public基本没有区别。

c# .net
5个回答
7
投票
public class CollectionOfChildren
{
    public Child this[int index] { get; }
    public void Add(Child c) {
        c.Parent = this;
        innerCollection.Add(c);
    }
}

public class Child
{
    public CollectionOfChildren Parent { get; internal set; }
}

2
投票

我的答案包含解决方案-第一个使用嵌套类,以允许内部类访问外部类。后来我意识到,如果精心设计了属性getter和setter以避免无限的间接递归,则无需访问其他类的私有数据,因此也不需要嵌套的classe。

为了避免internal字段出现问题,您可以将集合类嵌套到项目类中,然后将字段设置为private。以下代码并不完全符合您的要求,但是显示了如何创建一对多关系并使之保持一致。一个Item可能有一个父母和多个孩子。当且仅当某项具有父项时,该项才会在父项的子级集合中。我未经测试就编写了即席代码,但我认为没有办法打破Item类的视线。

public class Item
{
    public Item() { }

    public Item(Item parent)
    {
        // Use Parent property instead of parent field.
        this.Parent = parent;
    }

    public ItemCollection Children
    {
        get { return this.children; }
    }
    private readonly ItemCollection children = new ItemCollection(this);

    public Item Parent
    {
        get { return this.parent; }
        set
        {
            if (this.parent != null)
            {
                this.parent.Children.Remove(this);
            }
            if (value != null)
            {
                value.Children.Add(this);
            }
        }
    }
    private Item parent = null;

ItemCollection类嵌套在Item类内部,以访问私有字段parent

    public class ItemCollection
    {
        public ItemCollection(Item parent)
        {
            this.parent = parent;
        }
        private readonly Item parent = null;
        private readonly List<Item> items = new List<Item>();

        public Item this[Int32 index]
        {
            get { return this.items[index]; }
        }

        public void Add(Item item)
        {
            if (!this.items.Contains(item))
            {
                this.items.Add(item);
                item.parent = this.parent;
            }
        }

        public void Remove(Item item)
        {
            if (this.items.Contains(item))
            {
                this.items.Remove(item);
                item.parent = null;
            }
        }
    }
}

UPDATE

我现在(但只是粗略地)检查了代码,并且我相信它不会嵌套类就可以工作,但是我还不确定。都是使用Item.Parent属性而不会引起无限循环,但是已经存在的检查以及为效率而添加的检查可以避免这种情况-至少我相信。

public class Item
{
    // Constructor for an item without a parent.
    public Item() { }

    // Constructor for an item with a parent.
    public Item(Item parent)
    {
        // Use Parent property instead of parent field.
        this.Parent = parent;
    }

    public ItemCollection Children
    {
        get { return this.children; }
    }
    private readonly ItemCollection children = new ItemCollection(this);

[重要的是Parent属性,它将触发父级的子级集合的更新并防止进入无限循环。

    public Item Parent
    {
        get { return this.parent; }
        set
        {
            if (this.parent != value)
            {
                // Update the parent field before modifing the child
                // collections to fail the test this.parent != value
                // when the child collection accesses this property.
                // Keep a copy of the  old parent  for removing this
                // item from its child collection.
                Item oldParent = this.parent;
                this.parent = value;

                if (oldParent != null)
                {
                    oldParent.Children.Remove(this);
                }

                if (value != null)
                {
                    value.Children.Add(this);
                }
            }
        }
    }
    private Item parent = null;
}

ItemCollection类的重要部分是私有parent字段,该字段使项目集合了解其所有者以及触发所添加Add()属性更新的Remove()Parent方法或已删除的项目。

public class ItemCollection
{
    public ItemCollection(Item parent)
    {
        this.parent = parent;
    }
    private readonly Item parent = null;
    private readonly List<Item> items = new List<Item>();

    public Item this[Int32 index]
    {
        get { return this.items[index]; }
    }

    public void Add(Item item)
    {
        if (!this.items.Contains(item))
        {
            this.items.Add(item);
            item.Parent = this.parent;
        }
    }

    public void Remove(Item item)
    {
        if (this.items.Contains(item))
        {
            this.items.Remove(item);
            item.Parent = null;
        }
    }
}

1
投票

我最近以通用集合和要由子项实现的接口的形式实现了与AgileJon类似的解决方案:


0
投票

此序列对您有用吗?


0
投票

我最近也正在研究它,并考虑将这种关系真正地实施为尽可能防止错误。另外,我尝试保持其通用性并尽可能安全地输入。这可能只是过分的杀伤力,但我仍然想分享。

© www.soinside.com 2019 - 2024. All rights reserved.