In my C# .Net 4.0 composite pattern I want to have leafs that are generic. Most examples I found have a generic in the base node which propagates through the whole composite tree. I do not want that.
I have found the following solution (which I've stripped a bit to the essentials). An interface called INode which has two implementations. One called category which basically is a dictionary of INodes. It is a dictionary because I do not want duplicate leafs. The other implementation called ValueNode holds the information. This allows for differently typed leaf nodes.
public interface INode
{
string Name { get; }
}
public class CategoryNode : INode
{
public CategoryNode(string name)
{
this.Name = name;
this.Children = new Dictionary<string, INode>();
}
public string Name { get; private set; }
public List<string> Keys
{
get { return this.Children.Keys.ToList(); }
}
private Dictionary<string, INode> Children { get; set; }
public INode this[string key]
{
get { return this.Children[key]; }
}
public void Add(INode node)
{
this.Children.Add(node.Name, node);
}
}
public class ValueNode<T> : INode
{
public ValueNode(
string name,
T defaultValue)
{
this.Name = name;
this.Value = this.Default = defaultValue;
}
public ValueNode(
string name,
T defaultValue)
{
this.Name = name;
this.Value = this.Default = defaultValue;
}
public T Default { get; private set; }
public T Value { get; set; }
public string Name { get; private set; }
}
Notice that I've made the children list private so nobody can remove nodes. I am comfortable with this solution. However, the usage syntax it produces is a bit talkative. For example:
((this.root["category"] as CategoryNode)["leaf"] as ValueNode<int>).Value = (node as ValueNode<int>).Value;
While I had envisioned something like
this.root["category"]["leaf"] = node;
Does anybody have ideas for me to simplify the syntax?
How about adding an extension method to INode type ?
public static class INodeExtensions
{
public static void SetValue<T>(this INode node, string key, T v)
{
if(v is INode)
{
// category node set value
if(node is CategoryNode)
{
// convert and set value
}
else
{
throw new Exception("No children found.");
}
}
else
{
// value node set value
}
}
}