Теперь количество узлов больше, чем учитывается длина в цикле for, поэтому не все элементы будут повторяться.
Я не думаю, что это правда. Вы оцениваете element.ChildNodes.Length
не один раз, а на каждой итерации. Следовательно, если список активен, его длина будет меняться вместе с вашими изменениями.
Предположим следующую простую реализацию для вашего дерева:
class Node
{
readonly List<Node> children;
readonly String name;
public Node(String name)
{
this.children = new List<Node>();
this.name = name;
}
public Node AddChild(Node node)
{
children.Add(node);
return this;
}
public Node InsertChild(int index, Node node)
{
children.Insert(index, node);
return this;
}
public Int32 Length
{
get { return children.Count; }
}
public Node this[Int32 index]
{
get { return children[index]; }
}
public Int32 IndexOf(Node node)
{
return children.IndexOf(node);
}
public Node RemoveChild(Node node)
{
children.Remove(node);
return this;
}
public IEnumerable<Node> Children
{
get { return children.AsEnumerable(); }
}
public override String ToString()
{
var content = new String[1 + children.Count];
content[0] = name;
for (int i = 0; i < children.Count; )
{
var childs = children[i].ToString().Split(new [] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
content[++i] = "+ " + String.Join(Environment.NewLine + " ", childs);
}
return String.Join(Environment.NewLine, content);
}
}
Данный Node
содержит дочерние элементы (но не родительские) и простые методы для добавления, удаления, вставки, ..., дочерних элементов.
Давайте посмотрим, как мы могли бы построить хороший пример с таким Node
:
var root = new Node("Root");
root.AddChild(new Node("a")).
AddChild(new Node("b")).
AddChild(new Node("c").
AddChild(new Node("d").
AddChild(new Node("e")).
AddChild(new Node("f"))).
AddChild(new Node("g")).
AddChild(new Node("h"))).
AddChild(new Node("i"));
Результат вызова root.ToString()
будет выглядеть следующим образом.
Root
+ a
+ b
+ c
+ d
+ e
+ f
+ g
+ h
+ i
Полагаю, вы хотите сплющить дерево? Как уже было сказано, сделать это неизменным способом может быть хорошей идеей. Есть несколько способов сделать это, но с учетом API, описанного выше, мы можем прийти к следующему решению:
void Flatten(Node element, List<Node> nodes)
{
var before = nodes.Count;
foreach (var node in element.Children)
{
Flatten(node, nodes);
}
if (nodes.Count == before)
{
nodes.Add(element);
}
}
Почему я сдаю List<Node>
? Что ж, мы могли бы создать список при каждом вызове, который затем будет объединен со списком вызывающего абонента, однако версия выше немного более эффективна. Также мы используем свойство Count
, чтобы определить, были ли замечены какие-либо дочерние элементы. Мы также могли бы использовать метод расширения Any()
, но это снова ненужные накладные расходы. Мы в значительной степени просто проверяем, является ли данный узел листом. если да, то мы добавляем его в предоставленный список.
Если вы действительно хотите изменить исходное дерево, у вас есть другой вариант. Следующий код принимает элемент и рекурсивно просматривает его дочерние элементы. Листья остаются нетронутыми, дети с родителем присоединяют своих потомков к родителю.
void Flatten(Node element, Node parent = null)
{
for (var i = 0; i < element.Length; i++)
{
Flatten(element[i], element);
}
if (parent != null && element.Length > 0)
{
var children = element.Children.ToArray();
var index = parent.IndexOf(element);
parent.RemoveChild(element);
foreach (var child in children)
{
element.RemoveChild(child);
parent.InsertChild(index++, child);
}
}
}
Первая итерация не изменит значение element.Length
. Поэтому мы тоже могли спокойно его оценить один раз и все. Однако потенциальная вторая итерация сделает это. Вот почему мы сначала получаем копию element.Children.ToArray()
. Есть также другой способ без этой копии, который включает в себя обратный цикл for (переход от Length к -1).
Посмотрим, как будет выглядеть сериализация дерева после вызова Flatten(root)
.
Root
+ a
+ b
+ e
+ f
+ g
+ h
+ i
Надеюсь, этот ответ вам немного поможет.
person
Florian Rappl
schedule
17.09.2015
StreamReader
), создание копии или ведение списка на самом деле не сработает, по крайней мере, не так хорошо, как я могу сказать. - person Grant H.   schedule 13.08.2015