【问题标题】:How To Form my Node and Tree Generic Classes如何形成我的节点和树通用类
【发布时间】:2013-03-29 05:23:47
【问题描述】:

我有一个IList<Category>

类别类型来自我在 SQL Server 中的类别表:

表格:类别

CategoryID、ParentCategoryID

一张表中的典型层次结构。

因此,如果我有这个包含节点关系的类别 IList,那么我试图弄清楚这如何适合像很多人一样制作节点和树。以及在使用方面,构造函数的外观和创建此类的方式如何?

我认为在这里创建一个通用的 Node 类和 Tree 类是有意义的,这样我可以在将来将我认为用于其他树类型的重用。

那么,如果我有这样的东西,T 是如何被使用的,我有什么好处?

(这里是伪代码)

public class Node<T>
...
    Node<T> _parentNode;
    List<Node<T>> _children;

    private void SetParentNode(T)
    private void AddChild(T)

... etc.

试图理解这里的概念,即为什么泛型节点类将用于任何类型,如 Childeren 等,其中包含子/父关系 (int Ids)

更新

因此,这里对 GroupBy 建议有疑问。看看我试图用你的例子做什么:

首先,我的 Tree 类中有这个属性:

公共字典,IList>> ParentNodeAndRelatedChildrenFlattenedMap { get;私人套装; }

传入我的类的构造函数的是一个 IList 依赖项,我将其转换(循环并为每个依赖项创建一个新节点)到 IList>

现在我正在尝试按 Node.ParentId 对该列表进行分组,以便在父节点上进行分组,并且由于每个节点都有其 children 属性,因此很容易找出与这些父节点相关的子节点节点。

但现在我的代码中出现了问题:

public void CreateFlattenedMap()
{
    var parentGroups = _nodeDependencies.GroupBy(d => d.ParentNodeId);

    var dictionary = parentGroups.ToDictionary(d => d, d => d.ToList());

    ParentNodeAndRelatedChildrenFlattenedMap = dictionary;
}

好吧,它不喜欢我分配的字典,因为它是由 ToDictionary() 创建的 > 字典。所以不知道如何将这个分组分组到一个字典中,List> 其中字典中的节点是我分组的父节点的节点实例(是的,我在它的 Node.ParentId 上分组,但我想要节点虽然在最后的 ToDictionary 中)并且 List> 是来自 Parent Node.Children 属性的子节点列表。

【问题讨论】:

    标签: c# generics


    【解决方案1】:

    这是一个更完整的实现的样子:

    public class TreeNode<T>
    {
        private T _item;
        private TreeNode<T> _parentNode;
        private List<TreeNode<T>> _children;
    
        public TreeNode(T item)
        {
            _item = item;
        }
    
        public void SetParentNode(T parent)
        {
            _parentNode.Item = parent;
        }
    
        public T Item
        {
            get { return _item; }
            set { _item = value; }
        }
    
        public void AddChild(T child)
        {
            _children.Add(new TreeNode<T>(child));
        }
    
        public void RemoveChild(T child)
        {
            var node = _children.FirstOrDefault(e => e.Item.Equals(child));
            if (node != null)
                _children.Remove(node);
        }
    }
    

    还有你的问题:

    构造函数的外观和创建此类的使用情况如何?

    从上面AddChild()中可以看出,只需为构造函数指定子的类型即可。

    var node = new TreeNode<T>(item);
    

    那么,如果我有这样的东西,T 是如何被使用的,我从中受益了什么?

    如果做得好,泛型的使用同样会花费大量时间重写代码。在上面的实现中,我们可以通过简单地更改T 来制作我们想要的任何类型的树结构。因此,如果我们需要一种以上类型的树结构,这可以为我们节省大量时间。

    var intTreeNode = new TreeNode<int>(10);
    var stringTreeNode = new TreeNode<string>("hello world");
    

    通常在点击之前确实需要一些曝光时间,然后你“就明白了”,坚持下去。

    构造树

    要从可能有或没有父类别的类别列表构建树,您需要以某种方式遍历列表。解决这个问题的一个不错的方法是首先根据ParentCategoryID 将它们组织成分组并构建树。像这样的东西(未经测试):

    public List<TreeNode<Category>> ConstructCategories(List<Category> categories)
    {
        var groups = categories.GroupBy(e => e.ParentCategoryID);
        var rootGroup = groups.Single(e => e.Key == null);
        var categories = List<TreeNode<Category>>();
    
        foreach (var category in rootGroup)
        {
            // Create and fill category
            var node = new TreeNode<Category>(category);
            ConstructChildrenCategories(node, groups);
            categories.Add(node);
        }
    }
    
    public void ConstructChildrenCategories(TreeNode<Category> node, IEnumerable<IGrouping<Category>> groups)
    {
        var group = groups.Single(e => e.Key == node.Item.CategoryID);
    
        foreach (var category in group)
        {
            // Create and fill category
            var childNode = new TreeNode<Category>(category);
            ConstructChildrenCategories(childNode, groups);
    
            // We could do this automatically in both methods.
            childNode.SetParent(node.Item);
            node.AddChild(childNode);
        }
    }
    

    【讨论】:

    • 这就是问题所在,谢谢 bTW。如果我有包含 Category 和 ParentCategory 的关系列表,我将在哪里以及如何迭代该列表以查找节点的子节点?我的意思是当我创建一个新的 TreeNode 时,我需要能够迭代主列表以找到它的子列表...所以我会将整个 IList 传递到我的 TreeNode 以便我可以迭代它并添加到我的 TreeNode 的子列表中?
    • 是的,这是我在您更新之前考虑过这个问题后得出的结论。我必须遍历并为每一行创建一个新节点
    • 所以您的 Single on 组只获取该节点的那些子节点?
    • 是的,没错,我没有测试这个或任何东西,所以它可能有点偏离。
    • another thing public class Node where T : ICategory 将其限制为一种类型,类别。我希望我的节点类能够接受任何类型的关系自定义对象
    【解决方案2】:

    这可能会有所帮助:

    public interface ICategory
    {
        int Id { get; }
        int ParentId { get; }
    }
    
    public class Category : ICategory
    {
        int id;
        int parentId;
    
        public int Id { get { return id; }}
        public int ParentId { get { return parentId; }}
    }
    

    这是Node 类:

    public class Node<T> where T : ICategory
    {
        Node<T> _parentNode;
        List<Node<T>> _children;
    
        public Node<T> Parent { get { return _parentNode; }}
    
        public Node<T> Child(int index) { return _children[index]; }
    
        public T Value;
    
        public Node(T value)
        {
            this.Value = value;
        }
    
        public void AddChild(T item)
        {
            Node<T> child = new Node<T>(item);
            this._children.Add(child);
            child._parentNode = this;
        }
    }
    

    这是Tree 类:

    public class Tree<T> where T : ICategory
    {
        List<Node<T>> values;
    
        public Tree()
        {
            this.values = new List<Node<T>>();
        }
    
        public Node<T> FindNode(int id)
        {
            if (values.Exists(input => input.Value.Id == id))
            {
                return values.Find(input => input.Value.Id == id);
            }
            else { return null; }
    
        }
    
        public void AddNode(T value)
        {
            Node<T> parent = FindNode(value.ParentId);
    
            if (parent != null)
            {
                parent.AddChild(value);
            }
        }
    }
    

    【讨论】:

    • 再次通过 public class Tree where T : ICategory 和 public class Node where T : ICategory 将它们与预期的特定类型联系起来。我想要任何预期的类型。这样做的方法是创建一个需要 NodeId 和相关 ParentNodId 的 INode。然后我知道任何使用我的节点类或树类的东西都必须有一个 NodeId 和 ParentNodeId 可以使用。
    • 但是你确实给了我一些关于如何查找和添加节点的好主意,非常感谢
    • 什么是公共T值;价值代表什么?
    • @CoffeeAddict 您可能知道,这是一个简单的字段(我知道我们不应该将类的字段标记为 public,但为简单起见,我忽略了它)。
    • Value 不代表任何东西。我只是用它来保存每个节点的ICategory 的实际值。
    【解决方案3】:

    你不能把tree和treenode的所有功能都做成。

    而是将类数据添加到 Treenode。

    例如

    class MyClass
    {
        public int A;
        public string B;
    }
    
    ...
    
    TreeNode Node = TreeView.Nodes.Add("AAA");
    MyClass Data = new MyClass();
    Node.Tag = Data;
    

    分配给 TreeNode 的数据不会被删除。你也可以使用整个树属性和节点成员。

    在使用节点的数据时,唯一要做的就是进行类型转换。

    TreeNode Node = TreeView.Nodes[0];
    MyClass Temp = Node.Tag as MyClass;
    

    【讨论】:

      猜你喜欢
      • 2017-08-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多