LINQrecursion函数?

我们以这个n层深层结构为例:

public class SomeItem { public Guid ID { get;set; } public string Name { get; set; } public bool HasChildren { get;set; } public IEnumerable<SomeItem> Children { get; set; } } 

如果我正在寻找通过ID(在结构中的任何地方)得到一个特定的项目有一些LINQ善良,我可以用它来轻松地在一个单一的声明,或者我必须使用一些recursion函数如下:

  private SomeItem GetSomeItem(IEnumerable<SomeItem> items, Guid ID) { foreach (var item in items) { if (item.ID == ID) { return item; } else if (item.HasChildren) { return GetSomeItem(item.Children, ID); } } return null; } 

LINQ并不真正“做”很好的recursion。 你的解决scheme似乎是适当的 – 虽然我不确定HasChildren是真的需要…为什么不只是使用一个空的列表没有孩子的项目?

另一种方法是写一个DescendantsAndSelf和自我的方法,将返回所有的后代(包括项目本身),这样的事情;

 // Warning: potentially expensive! public IEnumerable<SomeItem> DescendantsAndSelf() { yield return this; foreach (var item in Children.SelectMany(x => x.DescendantsAndSelf())) { yield return item; } } 

但是,如果树很深,结果是非常低效,因为每个项目需要“通过”其祖先的所有迭代器。 Wes Dyer已经对此进行了博客 ,显示了更有效的实施。

无论如何,如果你有一个这样的方法(但是它的实现),你可以使用正常的“where”子句来find一个项目(或First / FirstOrDefault等)。

希望这可以帮助

 public static IEnumerable<Control> GetAllChildControls(this Control parent) { foreach (Control child in parent.Controls) { yield return child; if (child.HasChildren) { foreach (Control grandChild in child.GetAllChildControls()) yield return grandChild; } } } 

虽然有一些扩展方法可以在LINQ中实现recursion(可能看起来像你的函数),但是没有提供开箱即用的方法。

这些扩展方法的例子可以在这里或这里find。

我会说你的function是好的。

这是一个没有recursion的。 这样可以避免通过迭代器的几层代价,所以我认为它的效率和它们一样高。

  public static IEnumerable<T> IterateTree<T>(this T root, Func<T, IEnumerable<T>> childrenF) { var q = new List<T>() { root }; while (q.Any()) { var c = q[0]; q.RemoveAt(0); q.AddRange(childrenF(c) ?? Enumerable.Empty<T>()); yield return c; } } 

像这样调用:

  var subtree = root.IterateTree(x => x. Children).ToList(); 

记住你不需要用LINQ做任何事情,或者默认recursion。 当你使用数据结构时,有一些有趣的选项。 以下是一个简单的展平function,以防有人感兴趣。

  public static IEnumerable<SomeItem> Flatten(IEnumerable<SomeItem> items) { if (items == null || items.Count() == 0) return new List<SomeItem>(); var result = new List<SomeItem>(); var q = new Queue<SomeItem>(collection: items); while (q.Count > 0) { var item = q.Dequeue(); result.Add(item); if (item?.Children?.Count() > 0) foreach (var child in item.Children) q.Enqueue(child); } return result; }