2

I'm trying to make a tree structure of parents and children. The problem is that I only want to be able to assign the parent of a child in the child and parent classes, and nowhere else:

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

By a not-C# programmer, I was told to use friends (like in C++), but those aren't implemented in C#, and all my other solutions failed.

Waldi
  • 39,242
  • 6
  • 30
  • 78
Safron
  • 802
  • 1
  • 11
  • 23

3 Answers3

3

If you can also be responsible for actually creating the children and parents (you can provide factory methods for this), you can use interfaces and private classes to achieve this.

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

interface IParent
{
   // Methods and properties for parent
}

Now, you create a private implementation of IChild that also has a Parent setter. In your code call your private implementation, but only return IChild and IParent.

Note - private classes are nested classes in C#. I can't tell you in which class these classes should be nested - that depends on your project. If there's no such reasonable place you can create a child/parent DLL library, and have internal Child and Parent classes that implement the public interfaces.

Incidentally, I don't understand why you have both Parent and Child classes, and especially not why Child is derived from Parent. If you have one Node class, you can have a Parent property with a private setter, and not worry about it.

zmbq
  • 38,013
  • 14
  • 101
  • 171
  • The problem with a 'node' would be that there is no root with only children, that's why I created a parent object for this. But should I have a Node class, how would I implement a root? – Safron Jan 04 '15 at 09:58
  • 1
    Your root would simply have a parent set to null. If it's important for your class or consuming code, maybe expose an IsRoot() method on your node which checks if it has a parent and returns a boolean. – Joel Gregory Jan 04 '15 at 10:10
  • What @JoelGregory said. That's how tree nodes are usually implemented. – zmbq Jan 04 '15 at 11:02
2

This may not answer your question but it is an alternative. This is a node structure and you could use something like this:

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

I see @zmbq has just edited to suggest something similar.

Joel Gregory
  • 469
  • 4
  • 10
  • You all suggested this solution, so it was hard to choose wich one to accept, but I want to thank you all for answering so quickly. – Safron Jan 04 '15 at 10:15
1

If you require Parent and Child to be different classes, then you can do this using events. Make the Parent class offer an event whenever a Child is added and removed, then make the Child listen for this and sets its own parent appropriately. Effectively the child list in the Parent becomes the primary data and the parent becomes a back reference, like so:

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

(Of course, if they were the same class, then everything could be private and this mechanism would not be necessary.)

dbc
  • 104,963
  • 20
  • 228
  • 340