首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >用于在C#中创建树结构的紧凑语法

用于在C#中创建树结构的紧凑语法
EN

Stack Overflow用户
提问于 2015-11-15 05:35:06
回答 3查看 2.5K关注 0票数 3

我想以一种简洁的方式在代码中创建一个有点复杂的树结构。现在我正在使用这个(简化的):

代码语言:javascript
运行
复制
var root = new Tree();
var c1 = root.Add("1");
var c2 = root.Add("2");
var c21 = c2.Add("2-1");
var c22 = c2.Add("2-2");
//...

平均节点宽度为4,树高约为5,因此上述过程非常繁琐,更不用说可维护性差了。

让我们假设节点稍后不是按名称访问的。树/节点类可以根据需要进行调整。性能可以忽略不计。通过XML或类似的方式创建是不可能的(实际上节点构造函数要复杂得多)。

我正在寻找类似以下内容的东西,但我不确定如何在C#中实现它。在Java语言中,这可以通过匿名类来实现,而在C#中,匿名类不能用于此目的。

代码语言:javascript
运行
复制
var root = new Tree() {
    Add("1");
    Add("2") {
        Add("2-1");
        Add("2-2");
    };
}

我能想到的最好的解决方案是使用类似下面的run-on声明,我认为它的可维护性很差:

代码语言:javascript
运行
复制
// AddXxx returns the added node
var root = new Tree()
    .Add("1")
    .AddSibling("2")
        .Add("2-1")
        .AddSibling("2-2")
    .AddParent("3")
        .Add("3-1")

或者:

代码语言:javascript
运行
复制
// Add now always adds a sibling, Children/Parent steps up/down in the hierarchy
var root = new Tree()
    .Children
    .Add("1")
    .Add("2")
        .Children
        .Add("2-1")
        .Add("2-2")
    .Parent
    .Add("3")
        .Children
        .Add("3-1")
EN

回答 3

Stack Overflow用户

发布于 2015-11-15 05:51:58

我会这样做:

代码语言:javascript
运行
复制
public class Tree
{
    public string Name { get; private set; }
    public List<Tree> Trees { get; private set; }

    public Tree(string name)
    {
        this.Name = name;
        this.Trees = new List<Tree>();
    }

    public Tree(string name, params Tree[] nodes)
        : this(name)
    {
        if (nodes == null || !nodes.Any()) return;
        Trees.AddRange(nodes);
    }
}

然后像这样使用它:

代码语言:javascript
运行
复制
var trees = new List<Tree>
{
    new Tree("1"),
    new Tree("2",
        new Tree("2-1"),
        new Tree("2-2",
            new Tree("2-2-1"),
            new Tree("2-2-1")
        )
    ),
    new Tree("3",
        new Tree("3-1")
    )
};

我的灵感来自于XElement构造函数,它简化了XML节点的创建,使其具有更好的可读性。如果您正在使用XML,那么我建议您使用该类。

票数 6
EN

Stack Overflow用户

发布于 2015-11-15 05:42:44

您可以使用collection initializer

代码语言:javascript
运行
复制
class Node : IEnumerable<Node> // implement interface to taste
{
    public Node(string name)
    {
        ...
    }

    public void Add(Node n)
    {
        ...
    }
}

var root = new Node("x")
{   // Each item in this {} is passed to Add
    new Node("y-1")
    {
        new Node("z-1"),
        new Node("z-2")
    },
    new Node("y-2")
};
票数 3
EN

Stack Overflow用户

发布于 2015-11-15 06:06:28

一种传统的方法是使用集合初始化器。我并不热衷于此,因为这意味着您需要实现一个Add方法,这意味着您需要实现一个可变树接口,这是一个让许多传统树算法变得不安全的麻烦。(您也可以实现freezable TreeBuilder,但这同样麻烦)。

我更喜欢在构造时列出所有树节点,使用C# 6的using static可以非常清晰地做到这一点。

Linqpad查询:

代码语言:javascript
运行
复制
void Main()
{
    Node("test",
        Node("kid"),
        Node("kid2",
            Node("grandchild")
        ),
        Node("kid3",
            Node("grandchild2"),
            Node("grandchild3")
        )
    )
    .ToString().Dump();
}

public static class Tree
{
    public static TreeNode<T> Node<T>(T val, params TreeNode<T>[] kids)
        => new TreeNode<T>(val, kids);
}

public struct TreeNode<T>
{
    public readonly T NodeValue;
    public readonly IReadOnlyList<TreeNode<T>> Kids;

    public TreeNode(T val, TreeNode<T>[] kids)
    {
        NodeValue = val;
        Kids = kids;
    }

    public override string ToString() 
        => $"\n{NodeValue}:{string.Join("", Kids).Replace("\n", "\n  ")}";
}

打印的内容:

代码语言:javascript
运行
复制
test:
  kid:
  kid2:
    grandchild:
  kid3:
    grandchild2:
    grandchild3:

请注意,将数组重新解释为IReadOnlyList并不能保护您免受在构造之后更改参数数组的讨厌调用者的影响,这在普通项目中可能没什么问题,但对于公共api - YMMV来说可能就没那么热了。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33713647

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档