Here's an alternative approach that I use which doesn't require storing the entire hierarchy in a list, and isn't tied to a particular data structure:
private static IEnumerable<TResult> SelectRecursiveIterator<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, IEnumerable<TSource>> getChildren, Func<TSource, int, TResult> selector)
{
var stack = new Stack<IEnumerator<TSource>>();
try
{
stack.Push(source.GetEnumerator());
while (0 != stack.Count)
{
var iter = stack.Peek();
if (iter.MoveNext())
{
var current = iter.Current;
yield return selector(current, stack.Count - 1);
var children = getChildren(current);
if (children != null) stack.Push(children.GetEnumerator());
}
else
{
iter.Dispose();
stack.Pop();
}
}
}
finally
{
while (0 != stack.Count)
{
stack.Pop().Dispose();
}
}
}
public static IEnumerable<TResult> SelectRecursive<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TSource>> getChildren, Func<TSource, int, TResult> selector)
{
if (source == null) throw new ArgumentNullException("source");
if (selector == null) throw new ArgumentNullException("selector");
if (getChildren == null) return source.Select(s => selector(s, 0));
return SelectRecursiveIterator(source, getChildren, selector);
}
public static IEnumerable<T> SelectRecursive<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> getChildren)
{
if (source == null) throw new ArgumentNullException("source");
if (getChildren == null) return source;
return SelectRecursiveIterator(source, getChildren, (s, _) => s);
}
To get the list of all IDs, you would then use something like this:
List<int> allIds = groups.SelectRecursive(g => g.Groups).Select(g => g.Id).ToList();