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