Создание иерархии узлов дерева в C#


Следующая функция принимает список темы сущностей, извлеченных из базы данных, используя LINQ-в-сущности. В каждой теме есть идентификатор, название и атрибутом parentId.

Я хочу заполнить ASP.NET элемент управления TreeView, и поэтому функция создает иерархию темы, основанные на их атрибутом parentId. Если тема не имеет родителя, его атрибутом parentId имеет значение null, и я положил его под "корень".

public TreeNode GenerateTopicsTree(List<Topic> topics) {
  var s = new Stack<TreeNodeAndId>();
  var root = new TreeNode("Topics", "0");
  s.Push(new TreeNodeAndId { Id = null, TreeNode = root });
  while (s.Count > 0) {
    var node = s.Peek();
    var current = topics.FirstOrDefault(o => o.ParentId == node.Id);
    if (current == null) {
      s.Pop();
      continue;
    }
    var child = new TreeNode(current.Title, current.Id.ToString());
    node.TreeNode.ChildNodes.Add(child);
    s.Push(new TreeNodeAndId { Id = current.Id, TreeNode = child });
    topics.Remove(current);
  }
  return root;
}

struct TreeNodeAndId
{
  public TreeNode TreeNode;
  public int? Id;
}

Каких-либо улучшений?



27352
8
задан 9 октября 2011 в 01:10 Источник Поделиться
Комментарии
1 ответ

Просто слово советы, создавая изменчивую структуру, которая всегда плохая идея. Это должен быть класс в противном случае вы просто оставляете себя открытыми для проблем и путаницы.

Вы могли бы улучшить его гораздо больше, если не ограничивать свои методы, чтобы использовать списки темам. Потом можно легко применить рекурсию и написать его в более функциональном стиле, с простой логикой.

static TreeNode GenerateTopicsTree(IEnumerable<Topic> topics)
{
// shouldn't the root value be null?
var root = new TreeNode("Topics", null);
return GenerateTopicSubTree(root, topics);
}

static TreeNode GenerateTopicSubTree(TreeNode root, IEnumerable<Topic> topics)
{
// partition the topics to child and non-child topics
var rootId = GetId(root);
var childTopics = topics.ToLookup(topic => topic.ParentId == rootId);

// create and add subtrees to the current node
var childNodes = childTopics[true].Select(GenerateNode);
foreach (var childNode in childNodes)
{
root.ChildNodes.Add(GenerateTopicSubTree(childNode, childTopics[false]));
}
return root;
}

static int? GetId(TreeNode node)
{
int id;
if (Int32.TryParse(node.Value, out id))
return id;
return null;
}

static TreeNode GenerateNode(Topic topic)
{
return new TreeNode(topic.Title, Convert.ToString(topic.Id));
}

Рекомендуется использовать привязки данных для создания собственных деревьев. Я не знаю, как он работает с ASP.NET поэтому я не могу давать вам советы, как это сделать. Но это должны сделать этот шаг излишним, поскольку система будет генерировать дерево для вас. Вам, вероятно, придется создать класс для представления темы, организованные в иерархию, но вы можете использовать приведенный выше код, чтобы создать эту иерархию.


Подумав об этом, я думаю, было бы лучше, чтобы просто сгруппировать их все сразу в начале вместо разбиения на каждом шагу. Вот альтернативную реализацию:

static TreeNode GenerateTopicsTreeAlt(IEnumerable<Topic> topics)
{
var root = new TreeNode("Topics", null);

// group all children together now so we don't need to regroup them again later
var childTopics = topics.ToLookup(topic => topic.ParentId);
return GenerateTopicSubTreeAlt(root, childTopics);
}

static TreeNode GenerateTopicSubTreeAlt(TreeNode root, ILookup<int?, Topic> childTopics)
{
// create and add subtrees to the current node
var rootId = GetId(root);
var childNodes = childTopics[rootId].Select(GenerateNode);
foreach (var childNode in childNodes)
{
root.ChildNodes.Add(GenerateTopicSubTreeAlt(childNode, childTopics));
}
return root;
}

5
ответ дан 10 октября 2011 в 08:10 Источник Поделиться