Code Review! Parent-child relationships

D

Danny Tuppeny

Hi all,

I've been trying to write some classes, so when I have a parent-child
relationship, such as with Folders in my application, I don't have to
remember to add a parent reference, as well as adding to the child
collection, eg:

parent.Children.Add(child);
child.Parent = parent;

And I've come up with the below (I've included my test, and three classes).
It seems to work, but I have two questions....

1. Does this look like the best way of doing this?

2. I now want to extend this to support multiple lists (eg, my folders can
contain both folders, and messages. Messages can have child messages, and
therefore have both a parent folder, and a parent message), eg.

folder.ChildFolders // Returns List<TreeItem<Folder>>
folder.Messages // Returns List<TreeItem<Message>>

message.ParentMessage // Returns TreeItem<Message>
message.ParentFolder // Returns TreeItem<Folder>

Or should I create a base class that represents the entire tree (Folders and
Messages)? Though this way, I'd have a single child list, and would need to
add properties, like .ChildFolders, which would filter the .Children list!

I know it looks like a lot of code here, but it's just 3 classes and my
command line test. If you paste it into VS, it'll look dead simple :)

Any thoughts or suggestions?

Thanks,

Danny Tuppeny

namespace MyApplication
{
class Program
{
static void Main(string[] args)
{
Test1();
Test2();
}

static void Test1()
{
Folder parent = new Folder();
Folder child = new Folder();

child.Parent = parent;

if (parent.Children.Contains(child))
{
Console.WriteLine("Test succeeded");
}
else
{
Console.WriteLine("Test failed");
}
}

static void Test2()
{
Folder parent = new Folder();
Folder child = new Folder();

parent.Children.Add(child);

if (parent == child.Parent)
{
Console.WriteLine("Test succeeded");
}
else
{
Console.WriteLine("Test failed");
}
}
}





public class ChildList<T> : List<T>
{
public event EventHandler<ChildListEventArgs<T>> Added;
public event EventHandler<ChildListEventArgs<T>> Removed;

public class ChildListEventArgs<T> : EventArgs
{
public T Item;
public ChildListEventArgs(T item)
{
Item = item;
}
}

public new void Add(T item)
{
EventHandler<ChildListEventArgs<T>> added = Added;
if (added != null)
added(this, new ChildListEventArgs<T>(item));

base.Add(item);
}

public new void AddRange(IEnumerable<T> collection)
{
EventHandler<ChildListEventArgs<T>> added = Added;
if (added != null)
{
foreach (T item in collection)
{
added(this, new ChildListEventArgs<T>(item));
}
}

base.AddRange(collection);
}

public new void Insert(int index, T item)
{
EventHandler<ChildListEventArgs<T>> added = Added;
if (added != null)
added(this, new ChildListEventArgs<T>(item));

base.Insert(index, item);
}

public new void InsertRange(int index, IEnumerable<T> collection)
{
EventHandler<ChildListEventArgs<T>> added = Added;
if (added != null)
{
foreach (T item in collection)
{
added(this, new ChildListEventArgs<T>(item));
}
}

base.InsertRange(index, collection);
}

public new bool Remove(T item)
{
EventHandler<ChildListEventArgs<T>> removed = Removed;
if (removed != null)
removed(this, new ChildListEventArgs<T>(item));

return base.Remove(item);
}

public new void RemoveAll(Predicate<T> match)
{
EventHandler<ChildListEventArgs<T>> removed = Removed;
if (removed != null)
{
foreach (T item in base.FindAll(match))
{
removed(this, new ChildListEventArgs<T>(item));
}
}

base.RemoveAll(match);
}

public new void RemoveAt(int index)
{
EventHandler<ChildListEventArgs<T>> removed = Removed;
if (removed != null)
removed(this, new ChildListEventArgs<T>(base[index]));

base.RemoveAt(index);
}

new void RemoveRange(int index, int count)
{
EventHandler<ChildListEventArgs<T>> removed = Removed;
if (removed != null)
{
foreach (T item in base.GetRange(index, count))
{
removed(this, new ChildListEventArgs<T>(item));
}
}

base.RemoveAt(index);
}
}





public class TreeItem<T>
{
TreeItem<T> parent;
ChildList<TreeItem<T>> children;

public TreeItem()
{
children = new ChildList<TreeItem<T>>();
children.Added += delegate(object sender,
ChildList<TreeItem<T>>.ChildListEventArgs<TreeItem<T>> e) { e.Item.parent =
this; };
children.Removed += delegate(object sender,
ChildList<TreeItem<T>>.ChildListEventArgs<TreeItem<T>> e) { e.Item.parent =
null; };
}

public TreeItem<T> Parent
{
get { return parent; }
set
{
if (parent != value)
{
if (parent != null)
{
// This automagically unsets the internal parent
parent.children.Remove(this);
}
if (value != null)
{
// This automagically sets the internal parent
value.children.Add(this);
}
}
}
}

public ChildList<TreeItem<T>> Children
{
get { return children; }
}
}





public class Folder : TreeItem<Folder>
{
string name;

public Folder()
{
}

public string Name
{
get { return name; }
set { name = value; }
}

}
}
 
J

James Curran

um... HOw 'bout a nice simple:

parent.AddChild(child);

where AddChild is defined as

public AddChild(IChild pChild)
{
this.Children.Add(child);
child.Parent = this;
}

--
Truth,
James Curran
[erstwhile VC++ MVP]

Home: www.noveltheory.com Work: www.njtheater.com
Blog: www.honestillusion.com Day Job: www.partsearch.com
Danny Tuppeny said:
Hi all,

I've been trying to write some classes, so when I have a parent-child
relationship, such as with Folders in my application, I don't have to
remember to add a parent reference, as well as adding to the child
collection, eg:

parent.Children.Add(child);
child.Parent = parent;

And I've come up with the below (I've included my test, and three classes).
It seems to work, but I have two questions....

1. Does this look like the best way of doing this?

2. I now want to extend this to support multiple lists (eg, my folders can
contain both folders, and messages. Messages can have child messages, and
therefore have both a parent folder, and a parent message), eg.

folder.ChildFolders // Returns List<TreeItem<Folder>>
folder.Messages // Returns List<TreeItem<Message>>

message.ParentMessage // Returns TreeItem<Message>
message.ParentFolder // Returns TreeItem<Folder>

Or should I create a base class that represents the entire tree (Folders and
Messages)? Though this way, I'd have a single child list, and would need to
add properties, like .ChildFolders, which would filter the .Children list!

I know it looks like a lot of code here, but it's just 3 classes and my
command line test. If you paste it into VS, it'll look dead simple :)

Any thoughts or suggestions?

Thanks,

Danny Tuppeny

namespace MyApplication
{
class Program
{
static void Main(string[] args)
{
Test1();
Test2();
}

static void Test1()
{
Folder parent = new Folder();
Folder child = new Folder();

child.Parent = parent;

if (parent.Children.Contains(child))
{
Console.WriteLine("Test succeeded");
}
else
{
Console.WriteLine("Test failed");
}
}

static void Test2()
{
Folder parent = new Folder();
Folder child = new Folder();

parent.Children.Add(child);

if (parent == child.Parent)
{
Console.WriteLine("Test succeeded");
}
else
{
Console.WriteLine("Test failed");
}
}
}





public class ChildList<T> : List<T>
{
public event EventHandler<ChildListEventArgs<T>> Added;
public event EventHandler<ChildListEventArgs<T>> Removed;

public class ChildListEventArgs<T> : EventArgs
{
public T Item;
public ChildListEventArgs(T item)
{
Item = item;
}
}

public new void Add(T item)
{
EventHandler<ChildListEventArgs<T>> added = Added;
if (added != null)
added(this, new ChildListEventArgs<T>(item));

base.Add(item);
}

public new void AddRange(IEnumerable<T> collection)
{
EventHandler<ChildListEventArgs<T>> added = Added;
if (added != null)
{
foreach (T item in collection)
{
added(this, new ChildListEventArgs<T>(item));
}
}

base.AddRange(collection);
}

public new void Insert(int index, T item)
{
EventHandler<ChildListEventArgs<T>> added = Added;
if (added != null)
added(this, new ChildListEventArgs<T>(item));

base.Insert(index, item);
}

public new void InsertRange(int index, IEnumerable<T> collection)
{
EventHandler<ChildListEventArgs<T>> added = Added;
if (added != null)
{
foreach (T item in collection)
{
added(this, new ChildListEventArgs<T>(item));
}
}

base.InsertRange(index, collection);
}

public new bool Remove(T item)
{
EventHandler<ChildListEventArgs<T>> removed = Removed;
if (removed != null)
removed(this, new ChildListEventArgs<T>(item));

return base.Remove(item);
}

public new void RemoveAll(Predicate<T> match)
{
EventHandler<ChildListEventArgs<T>> removed = Removed;
if (removed != null)
{
foreach (T item in base.FindAll(match))
{
removed(this, new ChildListEventArgs<T>(item));
}
}

base.RemoveAll(match);
}

public new void RemoveAt(int index)
{
EventHandler<ChildListEventArgs<T>> removed = Removed;
if (removed != null)
removed(this, new ChildListEventArgs<T>(base[index]));

base.RemoveAt(index);
}

new void RemoveRange(int index, int count)
{
EventHandler<ChildListEventArgs<T>> removed = Removed;
if (removed != null)
{
foreach (T item in base.GetRange(index, count))
{
removed(this, new ChildListEventArgs<T>(item));
}
}

base.RemoveAt(index);
}
}





public class TreeItem<T>
{
TreeItem<T> parent;
ChildList<TreeItem<T>> children;

public TreeItem()
{
children = new ChildList<TreeItem<T>>();
children.Added += delegate(object sender,
ChildList<TreeItem<T>>.ChildListEventArgs<TreeItem<T>> e) { e.Item.parent =
this; };
children.Removed += delegate(object sender,
ChildList<TreeItem<T>>.ChildListEventArgs<TreeItem<T>> e) { e.Item.parent =
null; };
}

public TreeItem<T> Parent
{
get { return parent; }
set
{
if (parent != value)
{
if (parent != null)
{
// This automagically unsets the internal parent
parent.children.Remove(this);
}
if (value != null)
{
// This automagically sets the internal parent
value.children.Add(this);
}
}
}
}

public ChildList<TreeItem<T>> Children
{
get { return children; }
}
}





public class Folder : TreeItem<Folder>
{
string name;

public Folder()
{
}

public string Name
{
get { return name; }
set { name = value; }
}

}
}
 
D

Danny Tuppeny

James Curran said:
um... HOw 'bout a nice simple:

parent.AddChild(child);

where AddChild is defined as

public AddChild(IChild pChild)
{
this.Children.Add(child);
child.Parent = this;
}

Because that relies on remember not to use .Children.Add :)

If I can do some extra work now, which reduces the number of bugs my
software might include, it's worth it! :)

Though I guess, returning Children as a ReadOnlyList (or whatever it's
called) and then having an Add method would work.. I don't think it's as
nice though :)
 
J

James Curran

OK, plan 2 (A bit stripped down, but you should get the idea)

interface IParent
{
}

interface IChild
{
IParent Parent;
}

public class ChildList<T> : List<T>
{
private IParent oOwner;
public ChildList(IParent pOwner) : base()
{
oOwner = pOwner;
}

public new void Add(T item)
{
base.Add(item);
item.Parent = oOwner;
}
}


public class Folder : IParent, IChild
{
private IParent oParent;
prinvate ChildList<Folder> children;

public Folder()
{
children = ChildList<Folder>(this);
}

}



--
Truth,
James Curran
[erstwhile VC++ MVP]

Home: www.noveltheory.com Work: www.njtheater.com
Blog: www.honestillusion.com Day Job: www.partsearch.com
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top