Может быть, то, что я имею, так же хорошо, как и получается?
Это довольно хорошо. Мы можем сделать это немного лучше.
Для этого конкретного кода возвращение доходности внутри явного foreach действительно является правильным решением?
Это разумное решение. Это легко читать и четко исправить. Недостатком является, как я уже говорил ранее, производительность потенциально не очень хорошая, если дерево очень глубокое.
Вот как бы я это сделал:
static IEnumerable<T> AllNodes(this T root, Func<T, IEnumerable<T>> getChildren)
{
var stack = new Stack<T>();
stack.Push(root);
while(stack.Count > 0)
{
var current = stack.Pop();
yield return current;
foreach(var child in getChildren(current).Reverse())
stack.Push(child);
}
}
static void Main()
{
var operation = whatever;
var items = from op in operation.AllNodes(x=>x.NextOperations)
select GetItem(op);
foreach (var item in items)
{
}
}
Обратите внимание, что вызов метода Reverse () необходим, только если вы заботитесь о том, чтобы итерация прошла «по порядку». Например, предположим, что у операции Alpha есть дочерние операции Beta, Gamma и Delta, а у Delta есть дети Zeta и Omega. Обход идет так:
push Alpha
pop Alpha
yield Alpha
push Delta
push Gamma
push Beta
pop Beta
yield Beta
pop Gamma
yield Gamma
pop Delta
yield Delta
push Omega
push Zeta
pop Zeta
yield Zeta
pop Omega
yield Omega
и теперь стек пуст, так что мы закончили, и мы получаем элементы в порядке «предварительного заказа». Если вас не волнует порядок, если все, что вам нужно, это убедиться, что вы получите их все, не стесняйтесь поменять местами детей, и вы получите их в порядке Альфа, Дельта, Омега, Зета , Гамма, бета.
Имеет смысл?