通过使用yield return递归获取Tree-Structure的子项

时间:2015-11-09 08:53:51

标签: c# tree yield

我希望获得树状结构中所有成员的列表(包含n个包含n个组的组的组等等。根元素也包含成员。

架构:

Group
    Group
        Member
        Member
        Member
    Group
        Member
Group
    Member
    Member
Group
    Group
        Group
            Member

现在我的代码看起来像这样:

public IEnumerable<Member> GetMembers() {
     foreach(var member in this.MemberCollection) {
        yield return member;
     }
     foreach(var group in this.GroupCollection) {
        GetMembers();
     }
  }

不幸的是,这不起作用 - “GetMembers()”调用只是“被忽略” - 是否有“解决方法”?

我已经找到了与此类似的解决方案:

public IEnumerable<Member> GetMembers() {
 foreach(var group in this.GroupCollection) {     
     foreach(var member in GetMembers()) {
         yield return member;
     }
   }
}

2 个答案:

答案 0 :(得分:3)

要递归,你的方法应该带一个参数(否则递归永远不会停止):

public IEnumerable<Member> GetMembers(Group group) {
     foreach(var member in group.MemberCollection) {
        yield return member;
     }
     foreach(var subGroup in group.GroupCollection) {
        foreach (var member in GetMembers(group)) {
            yield return member;
        }
     }
  }

但是,对于深层嵌套的层次结构,递归迭代器块往往效率很低。更好的方法是使用迭代而不是递归。要像递归方法那样进行深度优先遍历,您可以这样做:

public IEnumerable<Member> GetMembers() {
     var stack = new Stack<Group>();
     stack.Push(this);
     while (stack.Count > 0) {
         var group = stack.Pop();
         foreach(var member in group.MemberCollection) {
            yield return member;
         }
         foreach(var subGroup in group.GroupCollection) {
            stack.Push(subGroup);
         }
     }
  }

要获得广度优先遍历,请使用队列而不是堆栈。

答案 1 :(得分:1)

简单地调用yield不会从调用方法返回其结果,如果它没有被枚举(并且它使用public IEnumerable<Member> GetMembers() { foreach(var member in this.MemberCollection) { yield return member; } foreach(var group in this.GroupCollection) { foreach(var member in group.GetMembers()) { yield return member; } } } ),它也不会被调用,而是你可以这样做:

yield

在方法中使用IEnumerable<>时,编译器实际上会生成一个单独的类来实现{{1}}以返回结果 - 该类只是懒惰地进行枚举,所以如果你实际上没有迭代结果,它们没有被评估(如果您依赖于可能在其他地方发生变异的属性的状态,那么在您枚举它们之前也不会对它们进行评估)