使用 yield return 递归获取树结构的子节点

Get children of a Tree-Structure recursively by using yield return

我想获得树状结构中所有成员的列表(一个包含 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()" 调用刚刚得到 "ignored" - 有 "Workaround" 吗?

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

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

简单地调用GetMembers不会return调用方法的结果,如果它没有被枚举(并且它使用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 时,编译器实际上会生成一个单独的 class 来实现 IEnumerable<> 到 return 您的结果 - class只懒惰地枚举,所以如果你实际上没有迭代结果,它们就不会被评估(并且也不会评估 直到 你枚举它们,如果你依赖 属性 可能在其他地方发生突变)

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

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

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