Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/293.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何最好地遍历子对象的子对象&x27;把孩子带到未知的深度?_C#_Linq - Fatal编程技术网

C# 如何最好地遍历子对象的子对象&x27;把孩子带到未知的深度?

C# 如何最好地遍历子对象的子对象&x27;把孩子带到未知的深度?,c#,linq,C#,Linq,想象一个具有以下特性的对象: class TestObject { public string Name { get; set; } public Collection<TestObject> Children { get; set; } } 以未知深度检索包含每个子级的结果的最佳方法是什么?Helper方法 评论 您可以轻松地修改遍历方法,使其完全按照您想要的方式运行。例如,如果您只需

想象一个具有以下特性的对象:

        class TestObject
        {
            public string Name { get; set; }
            public Collection<TestObject> Children { get; set; }
        }
以未知深度检索包含每个子级的结果的最佳方法是什么?

Helper方法 评论 您可以轻松地修改
遍历
方法,使其完全按照您想要的方式运行。例如,如果您只需要叶节点,那么您应该只
产生返回根子项
为null或空时,code>

性能问题

如果性能是任何问题,请考虑上面的LINQ/函数实现,或者看看Servy的答案,其中的任何一个都应该比使用<代码>收益>…/代码>的版本更有效。

< p>您可以编写这样的泛型遍历方法:

public static IEnumerable<T> Traverse<T>(T root, 
    Func<T, IEnumerable<T>> childSelector)
{
    var stack = new Stack<T>();
    stack.Push(root);

    while (stack.Any())
    {
        var next = stack.Pop();
        yield return next;
        foreach (var child in childSelector(next))
            stack.Push(child);
    }
}
公共静态IEnumerable遍历(T根,
Func(儿童选择器)
{
var stack=新堆栈();
栈.推(根);
while(stack.Any())
{
var next=stack.Pop();
其次是收益率;
foreach(childSelector中的var child(下一个))
栈.推(子);
}
}

这是一个通用模型,通常用于遍历树。请注意,这将执行深度优先搜索。如果您想进行呼吸优先搜索,您可以使用
队列
而不是
堆栈

如何初始化数据?上面的代码不会创建一个超过一级的树。应该避免递归迭代器块。与传统方法不同,它们相当“重”,并且与状态和构造/破坏关联更多。相反,编写一个非递归遍历函数将有相当明显的性能改进。@Servy Point做得很好。然而,OP可能对这里的性能一点也不感兴趣。我个人会从这个实现开始,只有当我发现它太慢的时候才会改变它;它被设计成通用的东西,你可以在很多地方使用。这是更有可能值得优化的东西。优化实际上使方法更简单、更容易使用、更灵活,而且性能更高,使决策更容易。@Servy优化并没有使方法更简单。您的方法看起来更简单的原因是,我正在执行的
null
检查没有出现在您的方法中。如果删除
null
检查,我的代码可以写4行。看一看编辑后的答案。@Servy另外,我上面的第三个实现(使用函数LINQ)应该更有效。使用堆栈非常有趣-处理一些性能数字来比较两个答案。谢谢。在我的性能测试中,与LINQ/递归方法相比,我得到了10-13%的性能提升。虽然性能不是最初问题的一部分,但值得注意。
// Works
var joe = person1.Children.SelectMany(c => c.Children).Concat(person1.Children);

// Does not work - only returns 1 level deep
var mary = person2.Children.SelectMany(c => c.Children).Concat(person2.Children);
public static IEnumerable<T> Traversal<T>(
    T root,
    Func<T, IEnumerable<T>> getChildren)
{
    if (root == null)
    {
        yield break;
    }

    yield return root;

    var children = getChildren(root);
    if (children == null)
    {
        yield break;
    }

    foreach (var child in children)
    {
        foreach (var node in Traversal(child, getChildren))
        {
            yield return node;
        }
    }
}

//Or if you don't need all those null checks, here's a more compact version.
public static IEnumerable<T> Traversal<T>(
    T root,
    Func<T, IEnumerable<T>> getChildren)
{
    yield return root;
    foreach (var child in getChildren(root))
        foreach (var node in Traversal(child, getChildren))
            yield return node;
}

//If you like a LINQ/functional style better, this is also equivalent.
public static IEnumerable<T> Traversal<T>(
    T root,
    Func<T, IEnumerable<T>> getChildren)
{
    return new T[] { root }
        .Concat(getChildren(root)
            .SelectMany(child => Traversal(child, getChildren)));
}
var everybody = Traversal(person, x => x.Children);
public static IEnumerable<T> Traverse<T>(T root, 
    Func<T, IEnumerable<T>> childSelector)
{
    var stack = new Stack<T>();
    stack.Push(root);

    while (stack.Any())
    {
        var next = stack.Pop();
        yield return next;
        foreach (var child in childSelector(next))
            stack.Push(child);
    }
}