parents 和 children 的树结构

Tree structure with parents and children

我正在尝试制作 parents 和 children 的树结构。问题是我只希望能够在 child 和 parent 类 中分配 child 的 parent,而别处:

public class Parent
{
    public static Parent Root = new Parent();
    
    private List<Child> children = new List<Child>();
    public ReadOnlyCollection<Child> Children
    {
        get { return children.AsReadOnly(); }
    }

    public void AppendChild(Child child)
    {
        child.Parent.RemoveChild(child);
        child.children.Add(child);
        child.Parent = this; //I need to asign the childs parent in some way
    }
    public void RemoveChild(Child child)
    {
        if (this.children.Remove(child))
        {
            child.Parent = Parent.Root; //here also
        }
    }
}
public class Child : Parent
{
    private Parent parent = Parent.Root;
    public Parent Parent
    {
        get { return this.parent; }
        private set { this.parent = value; } //nothing may change the parent except for the Child and Parent classes
    }
}

一位 not-C# 程序员告诉我使用 friends(比如在 C++ 中),但这些并没有在 C# 中实现,我的所有其他解决方案都失败了。

如果您还可以负责实际创建子项和父项(您可以为此提供工厂方法),则可以使用接口和私有 classes 来实现。

interface IChild
{
    // methods and properties for child, including
    IParent Parent { get; } // No setter.
}

interface IParent
{
   // Methods and properties for parent
}

现在,您创建 IChildprivate 实现,它也有一个父 setter。在您的代码中调用您的私有实现,但仅调用 return IChildIParent.

注意 - private classes 在 C# 中是嵌套的 classes。我无法告诉您这些 class 应该嵌套在哪个 class 中——这取决于您的项目。如果没有这样合理的地方,您可以创建一个 child/parent DLL 库,并让 internal 子级和父级 class 实现 public 接口。

顺便说一句,我不明白为什么你同时拥有 ParentChild class,尤其是不明白为什么 Child 派生自 Parent。如果你有一个 Node class,你可以有一个 Parent 属性 和一个私人 setter,不用担心。

这可能无法回答您的问题,但它是一种替代方法。这是一个节点结构,你可以使用这样的东西:

public class Node
{
    private Node _parent;
    private List<Node> _children = new List<Node>();

    public Node(Node parent)
    {
        _parent = parent
    }

    public ReadOnlyCollection<Node> Children
    {
        get { return _children.AsReadOnly(); }
    }

    public void AppendChild(Node child)
    {
        // your code
    }

    public void RemoveChild(Node child)
    {
        // your code
    }
}

我看到@zmbq 刚刚编辑建议类似的内容。

如果您需要 ParentChild 不同 class,那么您可以使用事件来实现。每当添加和删除 Child 时,让 Parent class 提供一个事件,然后让 Child 监听这个事件并适当地设置它自己的父级。实际上,Parent 中的子列表成为主要数据,而父列表成为反向引用,如下所示:

public class ParentChangedEventArgs : EventArgs
{
    public Parent Parent { get; private set; }
    public Child Child { get; private set; }

    public ParentChangedEventArgs(Parent parent, Child child)
    {
        this.Parent = parent;
        this.Child = child;
    }
}

public class Parent
{
    public static event EventHandler<ParentChangedEventArgs> ParentChanged; // Could be internal or protected.

    public static Parent Root = new Parent(); // SHOULDN'T THIS BE READONLY?

    private readonly List<Child> children = new List<Child>();

    public ReadOnlyCollection<Child> Children
    {
        get { return children.AsReadOnly(); }
    }

    public void AppendChild(Child child)
    {
        var oldParent = child.Parent;
        if (oldParent == this)
            return;
        oldParent.children.Remove(child);
        this.children.Add(child);
        if (ParentChanged != null)
            ParentChanged(oldParent, new ParentChangedEventArgs(this, child));
    }

    public void RemoveChild(Child child)
    {
        Root.AppendChild(child); // Removing a child means adding it to the root.
    }
}

public class Child : Parent
{
    static Child()
    {
        Parent.ParentChanged += new EventHandler<ParentChangedEventArgs>(Parent_ChildChanged);
    }

    static void Parent_ChildChanged(object sender, ParentChangedEventArgs e)
    {
        var child = e.Child;
        child.Parent = e.Parent;
    }

    private Parent parent = Parent.Root;

    public Parent Parent
    {
        get { return this.parent; }
        private set { this.parent = value; }
    }
}

(当然,如果它们是一样的class,那么一切都可以是私有的,这个机制就没有必要了。)