与普通叶子的综合样式

时间:2013-11-26 08:12:24

标签: c# generics design-patterns composite

在我的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;

有没有人想让我简化语法?

3 个答案:

答案 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)