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

Is there a way to declare members of an abstract base class that are the type of the derived class?

假设我有一个抽象基 class,我想在其中声明成员,该基将匹配派生自该基 class.[=26 的 classes 的类型=]

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
}

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

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

这可能看起来令人困惑和循环,但它确实可以编译。它基本上是说 parentT 类型,它必须派生自泛型 BaseClass。所以现在派生的 class 可以看起来像这样:

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. 一组类有继承关系
  2. 一组 类,可以包含第一组中的任何 类,并且具有类型严格的父项和子项。

首先,让我们声明第一组相互继承的类。暂时忽略 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.