关于c#:遍历对象的所有后代

Traversing all descendants of an object

我在遍历对象的所有后代时遇到问题。

下面代码中的'unit'在我的程序中是Unit类型。它有一个属性ChildUnits,返回该单元子代的List

我能成功地对孩子们进行手术。然后我检查这些孩子是否有孩子,如果有,我也可以对他们进行手术。

然而,我需要检查所有的后代,以防有更多的深度,而不仅仅是孙子。除了下面的代码外,我还尝试了while循环,但它变得非常混乱,所以我把它忽略了。

这是我恢复到的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
foreach (var child in unit.ChildUnits)
{
    //do something here with the child (I know it sounds dodgy).

    bool hasMoreChildren = child.ChildUnits.Count != 0;

    if(hasMoreChildren)
    {
        foreach (var descendant in child.ChildUnits)
        {
            //do something here with the descendant.
        }
    }
}

我可以再深入一个层次,因为对于一个单位来说,深度比这个要小的多。但这不是一个干净的解决方案。

我想我可能需要使用一个图遍历算法和/或递归,但我想得到一些关于如何最有效地解决这个问题的建议。

编辑:是否可以在不定义新函数/方法的情况下执行此操作?


Edit: Is it possible to do this without defining a new function/method?

你可以使用一个匿名的方法……我知道这并不是"不定义一个新方法"。

但是,还有一个问题你应该注意:循环引用…即使你不认为会有

这是一个实现,没有定义任何新方法

1
2
3
4
5
6
7
8
9
10
11
12
Action<IEnumerable<Unit>> process = null;
var processed = new HashSet<Unit>();
process = list => {
   foreach(var u in list.Where (processed.Add))
    {
        // do something here with u
        //... and then process children
        process(u.ChildUnits);
    }
};

process(myList); // do the actual processing


您可以使用递归:

1
2
3
4
5
6
7
8
void processChildren(List<Unit> children)
{
    foreach (var child in children)
    {
        //do something here with the child (I know it sounds dodgy).
        processChildren(child.Children); // recursive call here
    }
}

如果不想定义新方法,也可以滚动自己的堆栈:

1
2
3
4
5
6
7
8
9
10
11
var stack = new Stack<Unit>();
stack.push(firstUnit);
while( !stack.Any() ) {
    var item = stack.pop();
    //do something here with the item

    foreach(var child in item.Children)
    {
        stack.push(child);
    }
}


这样的算法:

1
2
3
4
def traverse(Unit i):
       for (Unit child : i.childList):
                  // Perform your logic for child
                  traverse(child)

这将为第一个节点的每个子节点执行相同的功能,当应用于i.child[j]时,它将为所有i.child[j].child[k]执行相同的功能,因此它将为每个节点及其所有子节点执行您想要的操作。

相反,您可以使用堆栈:

1
2
3
4
5
6
7
stack s;
s.push(firstNode);
while(!stack.empty()):
    t = stack.pop()
    foreach(Unit child : t):
        s.push(child)
        // Perform logic for child


另一种不使用递归或操作/lambda的方法是使用带有要处理的项的列表。

这样地。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var toDoList = new List<Unit> { unit };
while (toDoList.Any()) {
    // Get current child, and remove it from the to-do-list
    var currentChild = toDoList.First();
    toDoList.RemoveAt(0);

    // Do something with the current child.
    // ...

    // Now see, if the child has any children to handle
    if (currentChild.ChildUnits != null && currentChild.ChildUnits.Any()) {
        toDoList.AddRange(currentChild.ChildUnits);
    }
}