3

次のプロパティを持つオブジェクトを想像してください。

        class TestObject
        {
            public string Name { get; set; }
            public Collection<TestObject> Children { get; set; }
        }

ギザギザの方法でいくつかを初期化します。

var person1 = new TestObject(){ 
                          Name = "Joe", 
                          Children = new Collection<TestObject>(){ childCollection1 }; 
                              };

var person2 = new TestObject(){ 
                          Name = "Mary", 
                          Children = new Collection<TestObject>(){ childCollection2 }; 
                              };

Joe の childCollection は 1 レベルの深さしかありませんが、Mary の子供には子供がいて、その子にも子供がいます。

SelectMany を使用しようとしましたが、うまくいきませんでした。

// 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);

未知の深さまで、すべての子を含む結果を取得する最良の方法は何ですか?

4

2 に答える 2

5

ヘルパー メソッド

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);

コメント

Traversalメソッドを簡単に変更して、希望どおりに動作させることができます。たとえば、リーフ ノードのみが必要なyield return root;場合は、childrenが null または空の場合にのみ実行する必要があります。

パフォーマンスの問題

パフォーマンスが何らかの問題である場合は、上記の LINQ/機能の実装を検討するか、Servy の回答を参照してください。どちらも を使用したバージョンよりも効率的であるはずyield ...です。

于 2013-08-15T20:03:50.043 に答える