【问题标题】:Tree structure with parents and children与父母和孩子的树结构
【发布时间】:2015-03-02 01:52:55
【问题描述】:

我正在尝试制作父母和孩子的树状结构。问题是我只希望能够在子类和父类中分配一个孩子的父母,而不是其他地方:

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
    }
}

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

【问题讨论】:

  • 尝试使用受保护的。
  • 这有什么帮助?
  • Protected 是我已经尝试过的事情之一,但是我不能在子类中使用父类的其他实例的受保护字段而不是其自身。

标签: c# parent-child friend tree-structure


【解决方案1】:

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

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

interface IParent
{
   // Methods and properties for parent
}

现在,您创建一个IChildprivate 实现,它也有一个 Parent 设置器。在您的代码中调用您的私有实现,但只返回 IChildIParent

注意 - 私有类是 C# 中的嵌套类。我不能告诉你这些类应该嵌套在哪个类中——这取决于你的项目。如果没有这样合理的地方,您可以创建一个子/父 DLL 库,并拥有实现公共接口的internal Child 和 Parent 类。

顺便说一句,我不明白为什么你同时拥有 ParentChild 类,尤其不明白为什么 Child 是从 Parent 派生的。如果你有一个Node 类,你可以拥有一个带有私有设置器的Parent 属性,不用担心。

【讨论】:

  • “节点”的问题在于没有只有孩子的根,这就是为什么我为此创建了一个父对象。但是我应该有一个 Node 类,我将如何实现一个根?
  • 您的根目录只会将父级设置为空。如果它对你的类或消费代码很重要,可能会在你的节点上公开一个 IsRoot() 方法,该方法检查它是否有父节点并返回一个布尔值。
  • @JoelGregory 说了什么。这就是树节点通常的实现方式。
【解决方案2】:

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

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 刚刚编辑建议类似的东西。

【讨论】:

  • 你们都提出了这个解决方案,所以很难选择一个接受,但我要感谢大家这么快回答。
【解决方案3】:

如果您要求 ParentChild 是不同的类,那么您可以使用事件来做到这一点。让Parent 类在添加和删除Child 时提供一个事件,然后让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; }
    }
}

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多