一个 C# 扩展方法,用于递归选择所有后代






4.85/5 (11投票s)
如何使用扩展方法递归选择所有后代
引言
.NET 的 SelectMany
扩展方法仅选择源的直接子项。在这里,我提供两个名为 SelectManyRecursive
和 SelectManyAllInclusive
的 IEnumerable<T>
扩展方法。第一个将仅选择后代,而第二个将选择后代和源集合中的项目。我还包含一个测试程序来演示其工作原理。
Using the Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SelectManyRecursive
{
public static class Extensions
{
public static IEnumerable<T> SelectManyRecursive<T>
(this IEnumerable<T> source, Func<T, IEnumerable<T>> selector)
{
var result = source.SelectMany(selector);
if (!result.Any())
{
return result;
}
return result.Concat(result.SelectManyRecursive(selector));
}
public static IEnumerable<T> SelectManyAllInclusive<T>
(this IEnumerable<T> source, Func<T, IEnumerable<T>> selector)
{
return source.Concat(source.SelectManyRecursive(selector));
}
}
class Test
{
int _level;
public List<Test> Children = new List<Test>();
static Random rand = new Random(DateTime.Now.Millisecond);
public static int maxLevels = 5;
public static int total = 0;
public Test() : this(1)
{
}
private Test(int level)
{
++total;
_level = level;
if (level < maxLevels)
{
int numChildren = rand.Next(10) + 1;
++level;
while (numChildren-- > 0)
{
Children.Add(new Test(level));
}
}
}
}
class Program
{
static void Main(string[] args)
{
List<Test> root = new List<Test> { new Test(), new Test() };
var descendents = root.SelectManyRecursive(t => t.Children);
int descendantsCount = descendents.Count();
var all = root.SelectManyAllInclusive(t => t.Children);
var count = all.Count();
Console.WriteLine($@"Total Test instances = {Test.total}
Total descendents collected = {descendantsCount}.
Note that 'Total descendents' will correctly be {root.Count} less than
'Total Test instances' because SelectManyRecursive selects only the descendents.
SelectManyAllInclusive (count = {count}) will select all items including from the source.
Press enter to continue:");
Console.Read();
}
}
}
关注点
存在比此处介绍的算法更高效的非递归算法。
历史
- 2017 年 3 月 8 日:第一个版本
- 2017 年 3 月 9 日:更改为使用 result.Any() 代替 .ToList() 和 .Count,因为 .ToList() 可能会导致不必要的开销。