Если вам просто нужен способ перечисления дерева, вы можете реализовать его как генератор, это может выглядеть странно, вам, вероятно, лучше использовать пользовательский перечислитель, но по сути это одно и то же.
public interface IGetChildItems<TEntity>
{
IEnumerable<TEntity> GetChildItems();
}
public static IEnumerable<TEntity> Flatten<TEntity>(TEntity root)
where TEntity : IGetChildItems<TEntity>
{
var stack = new Stack<TEntity>();
stack.Push(root);
while (stack.Count > 0)
{
var item = stack.Pop();
foreach (var child in item.GetChildItems())
{
stack.Push(child);
}
yield return item;
}
}
Ограничение типа, в котором TEntity: IGetChildItems просто указывает на то, что вам необходимо абстрагироваться от спуска иерархии. Без приведенного выше кода не будет компилироваться.
Это будет сначала перечислять дерево по ширине, сначала будет давать родительский элемент, потом его потомки, а затем потомки этих потомков. Вы можете легко настроить приведенный выше код для достижения другого поведения.
Edit:
Материал возвращаемого результата сообщает компилятору, что он должен вернуть значение, а затем продолжить. yield является ключевым словом контекста, и он разрешен только внутри итеративного оператора. Генератор - это простой способ написания источника данных IEnumerable. Компилятор создаст конечный автомат из этого кода и создаст перечисляемый анонимный класс. Очевидно, ключевое слово yield не существует в VB.NET. Но вы все равно можете написать класс, который делает это.
Imports System
Imports System.Collections
Imports System.Collections.Generic
Public Class HierarchyEnumerator(Of TEntity As IGetChildItems(Of TEntity))
Implements IEnumerator(Of TEntity), IDisposable, IEnumerator
Public Sub New(ByVal root As TEntity)
Me.stack = New Stack(Of TEntity)
Me.stack.Push(root)
End Sub
Public Sub Dispose()
End Sub
Public Function MoveNext() As Boolean
Do While (Me.stack.Count > 0)
Dim item As TEntity = Me.stack.Pop
Dim child As TEntity
For Each child In item.GetChildItems
Me.stack.Push(child)
Next
Me.current = item
Return True
Loop
Return False
End Function
Public Sub Reset()
Throw New NotSupportedException
End Sub
Public ReadOnly Property Current() As TEntity
Get
Return Me.current
End Get
End Property
Private ReadOnly Property System.Collections.IEnumerator.Current As Object
Get
Return Me.Current
End Get
End Property
Private current As TEntity
Private stack As Stack(Of TEntity)
End Class