有没有办法声明抽象基类的成员是派生类的类型?

时间:2017-11-14 02:58:12

标签: c# generics inheritance types abstract-base-class

假设我有一个抽象基类,我想声明成员,它将匹配从这个基类派生的类的类型。

public abstract class BaseClass
{
    protected BaseClass parent;
}

public class DerivedClass1 : BaseClass
{
    // parent could be of type DerivedClass2
}

public class DerivedClass2 : BaseClass
{
    // parent could be of type DerivedClass1
}

这不起作用,因为每个派生类中的parent字段可以是从BaseClass派生的任何内容。我想确保parent中的DerivedClass1字段只能是DerivedClass1类型。所以我想也许我应该使用泛型。

public abstract class BaseClass<T> where T : BaseClass<T>
{
    protected T parent;
}

这可能看起来令人困惑和循环,但它确实编译。它基本上是说parent属于T类型,必须从通用BaseClass派生。所以现在派生类看起来像这样:

public class DerivedClass : BaseClass<DerivedClass>
{
    // parent is of type DerivedClass
}

问题在于,当我声明DerivedClass时,我必须自己强制执行类型匹配。没有什么可以阻止某人做这样的事情:

public class DerivedClass1 : BaseClass<DerivedClass2>
{
    // parent is of type DerivedClass2
}

C#是否有办法这样做,以便在基类中声明的成员类型肯定与派生类型匹配?

我认为这类似于这个C ++问题试图提出的问题:Abstract base class for derived classes with functions that have a return type of the derived class

1 个答案:

答案 0 :(得分:1)

如果我正确理解了您的要求,那么您将拥有一系列具有继承关系的类,并且您希望将它们排列在树结构中,其中每个实例都具有相同类型且具有相同类型的父类。这是一个有趣的问题。

稍微涂抹一下之后,我可以建议你将需求分成两个平行但相关的对象图,这样就可以了

  1. 一组具有继承关系的类
  2. 一组类,可以包含第一组中的任何类,并具有类型严格的父级和子级。
  3. 首先,让我们声明第一组彼此继承的类。暂时忽略Node位。

    public class BaseClass 
    {
        public Node ContainerNode { get; set; }
    }
    
    public class DerivedClass1 : BaseClass
    {
    }
    
    public class DerivedClass2 : BaseClass
    {
    }
    

    这些课程做得不多,但它只是一个例子。

    现在让我们设置另一组可以参与树的类。树中的每个元素称为Node

    //Basic node
    public class Node
    {
    }
    
    //A node that can contain a T (which must be a BaseClass or derived from one)
    public class Node<T> : Node where T : BaseClass
    {
        public T Parent { get; set; }
        public T This { get; set; }
    
        public Node(T innerClass) 
        {
            this.This = innerClass;
            innerClass.ContainerNode = this;
        }
    }
    

    现在,我们拥有了所需的一切,以加强您所寻求的类型安全性。我们可以在继承层次结构中创建类,如下所示:

        var child1 = new Node<DerivedClass1>(new DerivedClass1());
        var parent1 = new Node<DerivedClass1>(new DerivedClass1());
        child1.Parent = parent1.This;
    

    如果我们错误地混淆了DerivedClass1和DerivedClass2,那么看看会发生什么:

        var child2 = new Node<DerivedClass2>(new DerivedClass2());
        var parent2 = new Node<DerivedClass1>(new DerivedClass1()); //Oops
        child2.Parent = parent2.This;  //Does not compile
    

    正如您所看到的,Parent属性现在是类型安全的。

    现在所有的东西^^^^看起来都很混乱,所以让我们通过添加一些辅助方法来清理它。

    public class Node
    {
        public T GetParent<T>() where T : BaseClass
        {
            return ((Node<T>)this).Parent;
        }
    
        static public Node<T> Create<T>(T innerClass) where T : BaseClass
        {
            return new Node<T>(innerClass);
        }
    
        static public T GetParent<T>(T child) where T: BaseClass
        {
            return child.ContainerNode.GetParent<T>();
        }
    
        static public implicit operator T (Node<T> input)
        {
            return input.This;
        }
    }
    

    现在,由于编译器可以推断出<T>个参数,我们的声明更加简洁:

        var child1 = Node.Create(new DerivedClass1());
        var parent1 = Node.Create(new DerivedClass1());
        child1.Parent = parent1;
    

    任何派生类都可以轻松找到自己的父级:

    public class DerivedClass1 : BaseClass
    {
        protected DerivedClass1 Parent
        {
            get
            {
                return Node.GetParent(this);  //This is type safe!
            }
        }
    }
    

    对所有这一点的一个反对意见是,您不希望编码人员处理此节点层。嗯,他们没有,因为我们建立了一个隐含的演员:

        Node<DerivedClass1> a = Node.Create(new DerivedClass1());
        DerivedClass1 b = a;  //Works!!! And is type-safe.
    

    Full working code on DotNetFiddle