在我的C#.Net 4.0复合模式中,我希望有一些通用的叶子。我发现的大多数示例在基节点中都有一个泛型,它通过整个复合树传播。我不要那个。
我找到了以下解决方案(我已经删除了一些要点)。一个名为INode的接口,它有两个实现。一个叫做类别,它基本上是一个INode字典。这是一本字典,因为我不想要重复的叶子。名为ValueNode的另一个实现包含信息。 这允许不同类型的叶节点。
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; }
}
请注意,我已将子列表设为私有,因此没有人可以删除节点。 我很满意这个解决方案。但是,它产生的用法语法有点健谈。例如:
((this.root["category"] as CategoryNode)["leaf"] as ValueNode<int>).Value = (node as ValueNode<int>).Value;
虽然我设想了像
这样的东西this.root["category"]["leaf"] = node;
有没有人想让我简化语法?
答案 0 :(得分:1)
如何将扩展方法添加到INode类型?
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
}
}
}
答案 1 :(得分:1)
使用参数数组指定叶子的“路径”怎么样? 或者,如果您需要获取类别节点,还有另一种方法。
class CategoryNode : INode
{
public CategoryNode GetCategoryNode(params string[] path) {
CategoryNode cat = (CategoryNode)this.Children[path[0]];
for (int i = 1; i < path.Length; ++i) {
cat = (CategoryNode)cat.Children[path[i]];
}
return cat;
}
public ValueNode<T> GetLeafNode<T>(params string[] path) {
INode first = this.Children[path[0]];
if (path.Length == 1 && first is ValueNode<T>) return (ValueNode<T>)first;
CategoryNode cat = (CategoryNode)first;
for (int i = 1; i < path.Length - 1; ++i) {
cat = (CategoryNode)cat.Children[path[i]];
}
return (ValueNode<T>)cat.Children[path[path.Length-1]];
}
}
你这样使用它:
var leafNode = root.GetLeafNode<int>("cat1", "cat2", "leaf");
// or
root.GetLeafNode<int>("cat1", "cat2", "leaf").Value = 1234;
不再需要索引器。
答案 2 :(得分:0)
我最终得到了Teddy的建议并且还添加了GetValue。
另外,我将索引器放在INode接口中,只是在值节点上调用时抛出异常。这样您也可以使用this.root["category"]["leaf"]
语法。
您仍然必须强制转换为ValueNode&lt;&gt;如果你想访问value属性。但你可以做this.root["category1"]["category2"].SetValue<int>("leaf", 42)
。