Search code examples
c#partial-classes

simulating C++ friendship in C# with partial classes


I have the following situation:

I need to create a tree-like structure that once instantiated, is immutable .Think a list of list approach for instance. The problem is that during construction, I need to be able to insert nodes. So I need some sort of private interface that allows inserting nodes while the public interface of this structure only allows retrieving them. I tried this:

public partial class TreeLike {
  public SomeType1 SomeTreeProperty1 { get; private set; }
  public SomeType2 SomeTreeProperty2 { get; private set; }

  private List<NodeLike> _Nodes = new List<NodeLike>();
  public ReadOnlyCollection<NodeLike> Nodes {
    get {
      return _Nodes.AsReadOnly();
    }
  }
}

public class NodeLike {
  public SomeType1 SomeNodeProperty { get; private set; }

  private List<NodeLike> _Children = new List<NodeLike>();
  public ReadOnlyCollection<NodeLike> Children {
    get {
      return _Children.AsReadOnly();
    }
  }

  public partial class TreeLike {
    public SomeType3 SomeTreeProperty3 { get; private set; }

    TreeLike() {
      NodeLike n0 = new NodeLike();
      NodeLike n1 = new NodeLike();
      n0._Children.Add(n1);
      _Nodes.Add(n0);
    }
  }
}

The problem with this (besides the somewhat hackysh look of continuing TreeLike's declaration/definition inside NodeLike), is that while it works, SomeTreeProperty3of TreeLike is not visible to the outside world. That is, if I create an instance of TreeLike at the outmost scope, I can only access the first to properties which are declared in the "global" scope declaration of the partial class.

So I'm wondering if there's way to have properties and methods declared in the nested continuation of the partial class still be visible to the global scope (and therefore, clients of this class). Or if not, what would a better C#-idiomatic way to go about it be? Maybe creating immutable versions of the classes?


Solution

  • You should simply perform all initialization inside constructors, because it allows you to achive true immutability using readonly fields.

    For example, if your node interface is defined like this:

    interface INode
    {
        public string Name { get; }
        public ReadOnlyCollection<INode> Children { get; }
    }
    

    Then an implementation should simply be:

    class Node : INode
    {
        private readonly string _name;
        public string Name
        {
            get { return _name; }
        }
    
        private readonly ReadOnlyCollection<INode> _children;
        public ReadOnlyCollection<INode> Children
        {
            get { return _children; }
        }
    
        public Node(string name, IEnumerable<INode> children)
        {
            _name = name;
            _children = new List<INode>(children).AsReadOnly();
        }
    
        public Node(string name, params INode[] children)
            : this(name, (IEnumerable<INode>)children)
        { }
    }
    

    That last constructor overload uses the params keyword to allow you to pass child nodes directly through the constructor, which means you can do this:

    var root = new Node(
        "root",
        new Node("left",
            new Node("left-left"),
            new Node("left-right")),
        new Node("right",
            new Node("right-left"),
            new Node("right-right"))
        );