代码之家  ›  专栏  ›  技术社区  ›  Juliet

如何使通用类型和具体类型可互换?

  •  4
  • Juliet  · 技术社区  · 15 年前

    我正在编写一个不可变的二叉树类,其中所有方法(insert、remove、rotateleft等)都返回一个新的树实例,而不是就地修改它。

    我将创建许多不同的树实现:AVL树、红黑树、伸展树等。我有以下内容:

    public class AbstractBinaryTree<TreeType, T>
        where TreeType : AbstractBinaryTree<TreeType, T>
        where T : IComparable<T>
    {
        protected abstract TreeType CreateNode(TreeType left, T value, TreeType right);
        protected abstract T Value { get; }
        protected abstract TreeType Left { get; }
        protected abstract TreeType Right { get; }
        protected abstract bool IsNil();
    
        public TreeType Insert(T item)
        {
            if (this.IsNil())
            {
                return CreateNode(this, item, this);
                // ^ doesn't compile, can't convert type
                // AbstractBinaryTree<TreeType, T> to type TreeType
            }
            else
            {
                int compare = item.CompareTo(this.Value);
                if (compare < 0)
                {
                    return CreateNode(this.Left.Insert(item), this.Value, this.Right);
                }
                else if (compare > 0)
                {
                    return CreateNode(this.Left, this.Value, this.Right.Insert(Value));
                }
                else
                {
                    return this;
                    // ^ doesn't compile, can't converrt type
                    // AbstractBinaryTree<TreeType, T> to type TreeType
                }
            }
        }
    }
    

    这里的想法是abstractBinaryTree是一个树节点——不仅如此,它与 TreeType . 如果我能让上面的基类正常工作,那么我就可以这样写:

    public class AvlTree<T> : AbstractBinaryTree<AvlTree<T>, T>
    {
        public override AvlTree<T> Insert(T item) { return Balance(base.Insert(item)); }
    }
    

    以便我的insert方法返回 AvlTree<T> 而不是 AbstractBinaryTree<AvlTree<T>, T> . 但是,我甚至无法做到这一点,因为基类没有编译。

    如何将AbstractBinaryTree的实例传递到采用类型的方法中 树型 ?

    3 回复  |  直到 15 年前
        1
  •  2
  •   Tomas Petricek    15 年前

    我没有真正的答案-只是一些有用的提示。我认为这种语言的概念是 自我类型 (我找不到好的链接站点!)。总之,self类型意味着您可以声明一个抽象基类(例如 A )它可以有一个方法返回 自型 . 创建继承类时(例如 B )使用 自型 将参考 B (这很有趣,因为基类不知道这个类)。对于C 4风扇, 自型 是协变的。

    不管怎样,你可以尝试寻找一种方法来模仿 自我类型 在C中使用泛型…

    另一个指针指向一篇我以前看过的文章。据我所知,它使用泛型的方式和您使用的方式类似,所以它可能会给您一些提示,如何解决这个问题。

        2
  •  2
  •   CRice    15 年前

    使用 AbstractBinaryTree<TreeType, T>

        public abstract class AbstractBinaryTree<TreeType, T>
                where TreeType : AbstractBinaryTree<TreeType, T>
                where T : IComparable<T>
            {
                protected abstract TreeType CreateNode(AbstractBinaryTree<TreeType, T> left, T value, AbstractBinaryTree<TreeType, T> right);
                protected abstract T Value { get; }
                protected abstract TreeType Left { get; }
                protected abstract TreeType Right { get; }
                protected abstract bool IsNil();
    
                public virtual AbstractBinaryTree<TreeType, T> Insert(T item)
                {
                    if (this.IsNil())
                    {
                        return CreateNode(this.Left, item, this.Right);
                        // ^ doesn't compile, can't convert type 
                        // AbstractBinaryTree<TreeType, T> to type TreeType 
                    }
                    else
                    {
                        int compare = item.CompareTo(this.Value);
                        if (compare < 0)
                        {
                            return CreateNode(this.Left.Insert(item), this.Value, this.Right);
                        }
                        else if (compare > 0)
                        {
                            return CreateNode(this.Left, this.Value, this.Right.Insert(Value));
                        }
                        else
                        {
                            return this;
                            // ^ doesn't compile, can't converrt type 
                            // AbstractBinaryTree<TreeType, T> to type TreeType 
                        }
                    } 
                }
            }
    
        public class AvlTree<T> : AbstractBinaryTree<AvlTree<T>, T>
            where T : IComparable<T>
        {
            public override AbstractBinaryTree<AvlTree<T>, T> Insert(T item)
            {
                return base.Insert(item);
            }
    }
    

    使用balance()强制转换

    private AvlTree<T> Balance(AbstractBinaryTree<AvlTree<T>, T> item)
    {
        return (AvlTree<T>)item;
    }
    
    public override AbstractBinaryTree<AvlTree<T>, T> Insert(T item)
    {
        return Balance(Insert(item));
    }
    
        3
  •  2
  •   Juliet    15 年前

    哦,哇,我让事情对我自己来说太难了,但无论如何,解决方案都非常简单:

    AbstractBinaryTree 已经包含了一个Left、Value和Right属性,因此我可以使用 CreateNode(this.Left, this.Value, this.Right) 而不是试图返回 this :

    public abstract class AbstractBinaryTree<TreeType, T>
        where TreeType : AbstractBinaryTree<TreeType, T>
        where T : IComparable<T>
    {
        protected abstract TreeType CreateNil();
        protected abstract TreeType CreateNode(TreeType left, T value, TreeType right);
    
        protected abstract T Value { get; }
        protected abstract TreeType Left { get; }
        protected abstract TreeType Right { get; }
        protected abstract bool IsNil();
    
        public virtual TreeType Insert(T item)
        {
            if (this.IsNil())
            {
                // can't return 'this', so just creating a new nil node
                TreeType nil = CreateNil();
                return CreateNode(nil, item, nil);
            }
            else
            {
                int compare = item.CompareTo(this.Value);
                if (compare < 0)
                {
                    return CreateNode(this.Left.Insert(item), this.Value, this.Right);
                }
                else if (compare > 0)
                {
                    return CreateNode(this.Left, this.Value, this.Right.Insert(Value));
                }
                else
                {
                    // can't return 'this', so just creating a new node with a
                    // copy of the same values
                    return CreateNode(this.Left, this.Value, this.Right);
                }
            }
        }
    }
    
    public class AvlTree<T> : AbstractBinaryTree<AvlTree<T>, T>
    {
        public override AvlTree<T> Insert(T value) { return Balance(base.Insert(value)); }
    }
    

    avltree的实现非常好,因为我们在向下的过程中递归地插入到树中,并在调用堆栈展开时平衡树。

    如果有人能提出一种方法让我重用 我不想给一个新对象分配一个值的副本,我想听听,但目前看来这是可行的。