Код, который использует последовательность, в порядке. Как указывает spender, код, генерирующий перечисление, может иметь проблемы с производительностью, если дерево глубокое.
Предположим, в самой глубокой точке ваше дерево имеет четыре глубины; подумайте о том, что происходит с четырьмя глубокими узлами. Чтобы получить этот узел, вы выполняете итерацию корня, который вызывает итератор, который вызывает итератор, который вызывает итератор, который передает узел обратно в код, который передает узел обратно в код, который передает узел обратно ... Вместо того, чтобы просто Передавая узел вызывающей стороне, вы создали небольшую бригаду с четырьмя парнями, и они перемещают данные от объекта к объекту, прежде чем он, наконец, попадает в цикл, который его хотел.
Если дерево всего четыре в глубину, вероятно, ничего страшного. Но предположим, что дерево состоит из десяти тысяч элементов и имеет тысячу узлов, образующих связанный список сверху, а остальные девять тысяч узлов снизу. Теперь, когда вы повторяете эти девять тысяч узлов, каждый из них должен пройти тысячу итераторов, всего девять миллионов копий, чтобы получить девять тысяч узлов. (Конечно, вы, вероятно, получили ошибку переполнения стека и также потерпели крах процесса.)
Способ решения этой проблемы, если он у вас есть, - это управлять стеком самостоятельно, а не помещать в стек новые итераторы.
public IEnumerable<Effect> EffectsNotRecursive()
{
var stack = new Stack<Effect>();
stack.Push(this);
while(stack.Count != 0)
{
var current = stack.Pop();
yield return current;
foreach(var child in current.Effects)
stack.Push(child);
}
}
Исходная реализация имеет временную сложность O (nd), где n - количество узлов, а d - средняя глубина дерева; поскольку d в худшем случае может быть O (n), а в лучшем случае O (lg n), это означает, что алгоритм находится между O (n lg n) и O (n ^ 2) во времени. Это O (d) в пространстве кучи (для всех итераторов) и O (d) в пространстве стека (для всех рекурсивных вызовов.)
Новая реализация имеет временную сложность O (n), O (d) в пространстве кучи и O (1) в пространстве стека.
Одним из недостатков этого является то, что порядок отличается; дерево пересекается сверху вниз и справа налево в новом алгоритме, а не сверху вниз и слева направо. Если это вас беспокоит, вы можете просто сказать
foreach(var child in current.Effects.Reverse())
вместо.
Более подробный анализ этой проблемы см. В статье моего коллеги Уэса Дайера на эту тему:
http://blogs.msdn.com/b/wesdyer/archive/2007/03/23/all-about-iterators.aspx