代码之家  ›  专栏  ›  技术社区  ›  Andrew Russell

如何使C中的入侵树类使用泛型?

  •  4
  • Andrew Russell  · 技术社区  · 15 年前

    在C我有一个 侵入的 树结构如下:

    public abstract class Node
    {
        Container parent;
        Node nextNode;
        Node previousNode;
    
        public abstract class Container : Node
        {
            Node firstChild;
            Node lastChild;
        }
    }
    

    可以添加到树中的各种对象继承自 Node Container 取决于他们是否可以生孩子。

    通过制作 集装箱 一个内部类,它意味着它可以访问 结点 用于管理容器的子级列表。

    这一切都很好。但现在我希望它是通用的,这样我可以在维护类型安全的同时重用它——基本上将所有树功能移动到节点上方的通用类,以及节点和容器之间的其他类。下面是我要做的事情的大致设计:

    public abstract class GenericNode<Node, Container>
        where Node : GenericNode<Node, Container>
        where Container : GenericNode<Node, Container>.GenericContainer
    {
        Container parent;
        Node nextNode;
        Node previousNode;
    
        public abstract class GenericContainer : Node
        {
            Node firstChild;
            Node lastChild;
        }
    }
    

    当然,这不起作用,因为你不能 GenericContainer 继承自 结点 编译错误 CS0689 )即使我放弃了内部类需求(比如 internal 只是在我自己的库中小心一点)我仍然无法找出一个设计不会遇到同样的问题(和错误)。

    (我不认为我必须这样做,但我只是想说清楚: 我不想“修复”编译错误 我也不想寻找一个简单的树实现。这是一个容器设计问题。)

    所以现在我有点头疼。有人对如何设计这个东西有更好的想法吗?

    编辑:一定要看看 this answer 这是设计的另一个障碍,它试图使用扩展方法来避免将类“注入”到继承层次结构中的问题(但不幸的是不能完全工作)。

    6 回复  |  直到 15 年前
        1
  •  1
  •   Leon van der Walt    15 年前

    {
        MyNode n = new MyNode();
        var c = new MyNode.MyContainer();
        c.AddChild(n);
    
        MySubNode s = new MySubNode();
        c.AddChild(s);
    
        OtherNode o = new OtherNode();
        o.AddChild(o);
    
        //compiler doesn't allow this, as you'd expect:
        //c.AddChild(o);
    }        
    
    public interface IContainer<TContainerType, TNodeType>
        where TNodeType : GenericNode<TContainerType, TNodeType>
        where TContainerType : TNodeType, IContainer<TContainerType, TNodeType>
    {
    }
    
    public static class ContainerExtensions
    {
        public static void AddChild<TContainerType, TNodeType>(this IContainer<TContainerType, TNodeType> self, TNodeType node)
            where TNodeType : GenericNode<TContainerType, TNodeType>
            where TContainerType : TNodeType, IContainer<TContainerType, TNodeType>
        {
            GenericNode<TContainerType, TNodeType>.AddChild(self as TContainerType, node);
        }
    }
    
    public class GenericNode<TContainerType, TNodeType>
        where TNodeType : GenericNode<TContainerType, TNodeType>
        where TContainerType : GenericNode<TContainerType, TNodeType>
    {
        TContainerType parent;
        TNodeType nextNode;
        TNodeType previousNode;
    
        // Only used by Container
        TNodeType firstChild;
        TNodeType secondChild;
    
        internal static void AddChild(TContainerType container, TNodeType node)
        {
            container.firstChild = node;
            node.parent = container;
        }
    }
    
    public class MyNode : GenericNode<MyContainer, MyNode>
    {        
    }
    
    public class MyContainer : MyNode, IContainer<MyContainer, MyNode>
    {
    }
    
    public class MySubNode : MyNode
    {
    }
    
    public class OtherNode : GenericNode<OtherNode, OtherNode>, IContainer<OtherNode, OtherNode>
    {
    }
    
        2
  •  0
  •   Sunny    15 年前

    public class Tree<T> : ITree<T> where T : INode{
        public T RootNode { get; private set; }
        public Tree(T rootNode){
            RootNode = rootNode;
        }
    }
    
    public interface ITree<T> where T : INode{
        T RootNode { get; }
    }
    
    public interface INode{
        INode Parent { get; }
        List<INode> Children { get; }
    }
    
    internal class Node : INode{
        public INode Parent { get; private set; }
        public List<INode> Children { get; private set; }
        public Node( INode parent, List<INode> children = new List<INode>()){
            Parent = parent;
            Children = children;
        }
    }
    

        3
  •  0
  •   Lunivore    15 年前

    public abstract class GenericNode<Node, Container>
        where Node : GenericNode<Node, Container>
        where Container : GenericNode<Node, Container>.GenericContainer<Node>
    {
        Container parent;
        Node nextNode;
        Node previousNode;
    
        public abstract class GenericContainer<Branch> where Branch: GenericNode<Node, Container> 
        {
            private Leaf firstChild;
            private Leaf secondChild;
        }
    }
    

    Branch Node GenericNode

        4
  •  0
  •   Dan Bryant    15 年前

    public interface ITagged<T>
    {
        T Tag { get; set; }
    }
    
    public sealed class Tree<T>
    {
        //All Tree operations are performed here (add nodes, remove nodes, possibly move nodes, etc.)
        //Nodes are only exposed as 'ITagged<T>', such as:
        public ITagged<T> Root { get; private set; }
    
        public IEnumerable<ITagged<T>> GetChildren(ITagged<T> item)
        {
            //Cast to Container and enumerate...
        }
    
        //Several other tree operations...
    
        private class Node : ITagged<T>
        {
            Container parent;
            Node nextNode;
            Node previousNode;
    
            public T Tag { get; set; }
        }
    
        private class Container : Node
        {
            Node firstChild;
            Node lastChild;
        }
    }
    

        5
  •  0
  •   Andrew Russell    15 年前

    public abstract class GenericNode<Node, Container>
        where Node : GenericNode<Node, Container>
        where Container : Node
    {
        Container parent;
        Node nextNode;
        Node previousNode;
    
        // Only used by Container
        Node firstChild;
        Node secondChild;
    
        public static class ContainerHelpers
        {
            public static void AddChild(Container c, Node n)
            {
                c.firstChild = n; // not a real implementation ;)
                n.parent = c;
            }
        }
    }
    
    // EDIT: This does not work correctly! (see example below)
    public static class GenericNodeExtensionMethods
    {
        public static void AddChild<Node, Container>(this Container c, Node n)
            where Node : GenericNode<Node, Container>
            where Container : Node
        {
            GenericNode<Node, Container>.ContainerHelpers.AddChild(c, n);
        }
    }
    
    //
    // Example Usage
    //
    
    public class MyNode : GenericNode<MyNode, MyContainer>
    {
    }
    
    public class MyContainer : MyNode
    {
    }
    
    public class MySubNode : MyNode
    {
    }
    
    public class OtherNode : GenericNode<OtherNode, OtherNode>
    {
    }
    
    
    class Program
    {
        static void Main(string[] args)
        {
            MyNode n = new MyNode();
            MyContainer c = new MyContainer();
            c.AddChild(n);
    
            MySubNode s = new MySubNode();
            //
            // This does not work because it tries to fill the generic in the
            // extension method with <MySubNode, MyContainer>, which does not
            // fulfil the constraint "where Container : Node".
            //
            //c.AddChild(s);
    
            OtherNode o = new OtherNode();
            o.AddChild(o);
        }
    }
    

        6
  •  0
  •   Claus Jørgensen    15 年前

    public abstract class GenericNode<TNode, TContainer>
        where TNode : GenericNode<TNode, TContainer>
        where TContainer : GenericNode<TNode, TContainer>.GenericContainer
    {
        public TContainer Parent { get; set; }
        public TNode Next { get; set; }
        public TNode Previous { get; set; }
    
        public abstract class GenericContainer : GenericNode<TNode, TContainer>
        {
            public TNode FirstChild { get; set; }
            public TNode LastChild { get; set; }
        }
    }
    

    推荐文章