views:

1485

answers:

5

I am implementing a tree think of it as a folder structure so I have a class that looks like:

public class Folder
{
    //Various Props like Name etc.
    public IList<Folder> Children{get;}
    public Folder Parent {get;}
}

Now what I want is to be able to walk up and down the tree so given a root I can find a leaf, and given a leaf I can find the root node. So each child needs a parent. Now the question is what is the best way to add a new node to the tree. I have used two solutions in the past:

  1. Add an AddChild(Folder) method to Folder which handles adding the folder, and can set the parent. The problem with this is I now have to lock my Children collection so you can't bypass this method.
  2. Create my own Children collection which will be given a reference back to the instance, so it can handle setting the parent on the add. The problem with this I have to implement a new collection.
  3. Use a collection which has events when items are added or deleted.

I am curious what patterns people generally use, and then if anyone has any suggestions for my specific use case. I am using nHibernate to persist my tree to SQL server. I'd rather not implement a custom collection as it's a lot of code to get this to work for something which is a very small part of my application.

+1  A: 
Harper Shelby
So how would you prevent them from modifying the collection? What strategy do you use? ReadOnlyCollection? Return Copy of Collection?
JoshBerke
@Josh:One way to return an IEnumerable:public IEnumerable<Folder> Children{ get { foreach (Folder child in children) { yield return child; } }}
SchaeferFFM
+1  A: 

I would go with option 1 and then make the Children property look like this:

    public IEnumerable<Folder> Children
    {
        get { return this.children.GetEnumerator(); }
    }

Now AddChild must be called to add the children. The collection is not accessible.

Jack Ryan
Hmm nice works well so long as the client doesn't want to index into the IList<>...breaks down if your child is a dictionary...
JoshBerke
+1  A: 

Go with number 1, but make your Children property IEnumerable so that users can't add to the collection.

Rob Prouse
+3  A: 

After looking on MSDN you could try this:

List<Folder> children;

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

If your private member must be declared as an IList then we can copy that into a list and then return it. But I really don't see a problem with using a concrete implementation as a private member. Changing the implementation later won't break compatibility.

IList<Folder> children;

public ReadOnlyCollection<Folder> Children
{
    get 
    { 
        return new List<Folder>(this.children).AsReadOnly(); 
    }
}
Jack Ryan
Only if your using a List;-) IList doesn't support that.
JoshBerke
True. But as far as I can see there is no interface that specifices a readonly list. So you either go with the more generic IEnumerable and lose indexing. Or return a descendant of ReadOnlyList and change your private implementation. Any other solution requires writing your own classes/interfaces
Jack Ryan
Ive added a new solution that fits all your requirements :-)
Jack Ryan
+1  A: 

Implementing a custom collection's a lot of work; implementing a wrapper to an existing collection class that exposes only two or three methods isn't. And it seems like that's what you're looking for, judging from your response to JayArr. Something like:

public class ChildCollection
{
   // _Children is maintained by the Folder class, hence the internal access specifier
   internal Dictionary<KeyType, Folder> _Children = new Dictionary<KeyType, Folder>;

   public this[KeyType key]
   {
      get
      {
          return _Children[key];
      }
   }

   public IEnumerable<KeyType> Keys
   {
      get
      {
         return _Children.Keys;
      }
   }
}
Robert Rossney