Оборачиваю голову вокруг IEnumerable и IEnumerator

Итак, возвращаюсь к программированию после долгого перерыва в работе сетевым администратором... и изучаю C# (и Unity) по ходу дела. Пытаюсь уложить в голове IEnumerable и IEnumerator

Теперь вот пример кода класса, который я использую для тестирования/выяснения... он работает, рекурсивно создает узлы дерева/листа, однако я застрял в их извлечении

public class NodeTest : IEnumerable
{
   public int Counter;
   public List<NodeTest> children;

   public NodeTest(int count)
   {
        Counter = count;
        Debug.Log("Creating " + count);
        NodeTest tmpNode;

     if(count>1)
     {
        children = new List<NodeTest>();
        tmpNode = new NodeTest(Counter - 1);
        children.Add(tmpNode);
        tmpNode = new NodeTest(Counter - 1);
        children.Add(tmpNode);      
     }
 }

 public IEnumerator GetEnumerator()
 {
     foreach(NodeTest n in this.children)
     {
         yield return n;
     }
 }
}

Затем в другом месте я использую:

foreach(NodeTest nt in tstNode)
{
   Debug.Log(nt.Counter);
}

Который отлично извлекает дочерние элементы верхнего узла, но не перебирает дочерние элементы, чтобы получить их дочерние элементы и т. д. Я предполагаю, что мне нужно сделать больше в методе IEnumerator GetEnumerator?


person Jeff Jubinville    schedule 17.08.2018    source источник
comment
Если все ваши элементы относятся к типу NodeTest, почему вы решили использовать интерфейс IEnumerable вместо более конкретного и простого в использовании интерфейса IEnumerable‹NodeTest›, который также позволит вам легко использовать LINQ с вашими результатами.   -  person ckuri    schedule 17.08.2018


Ответы (1)


Вам нужно сделать NodeTest.GetEnumerator рекурсивным методом. Помимо возврата каждого дочернего элемента, вам нужно, чтобы каждый дочерний элемент возвращал всех своих дочерних элементов. (Что заставит ребенка каждого ребенка вернуть своих детей и т. д.).

public IEnumerator GetEnumerator()
{
    foreach(NodeTest n in this.children)
    {
       yield return n;
       foreach(NodeTest descendent in n)
       {
           yield return descendent;
       }
    }
}
person Andrew Shepherd    schedule 17.08.2018
comment
Ударь меня. Обратите внимание, что это возвращает родителя перед потомками — в зависимости от бизнес-требований вы можете переместить yield return n; ниже внутреннего foreach. - person Comintern; 17.08.2018
comment
О боже, почему я не подумал об этом ... спасибо, Андрей и Коминтерн - person Jeff Jubinville; 17.08.2018
comment
Следует отметить, что этот код правильный, но следует избегать использования рекурсивных возвратов yield, поскольку они могут стать крайне неэффективными. Если вы выполняете рекурсию на 20 уровней в глубину, это означает, что каждый элемент должен быть передан обратно через 20 уровней доходности, чтобы добраться до исходного foreach, а затем вернуться на 20 уровней, чтобы перейти к следующему элементу. Хотя, если вы рекурсируете только на несколько уровней, это не должно иметь большого значения. - person Bryce Wagner; 17.08.2018
comment
Да, в лучшем случае это будет 5-9 слоев в глубину... BSP Tree. Код Эндрю выдает мне ошибку, поскольку у меня были дочерние элементы = новый список‹NodeTest› перед проверкой, достиг ли я предела моей рекурсии. - person Jeff Jubinville; 17.08.2018