-2

假设我有一个抽象基类,我想在其中声明与派生自该基类的类的类型匹配的成员。

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. 我想确保parentin 中的字段DerivedClass1只能是 type DerivedClass1。所以我在想也许我应该使用泛型。

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

这可能看起来令人困惑和循环,但它确实可以编译。它基本上是说parentisT必须从 generic 派生的类型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

4

1 回答 1

1

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

在考虑了一下这个之后,我建议你将需求分成两个平行但相关的对象图,这样你就有了

  1. 一组具有继承关系的类
  2. 一组类,可以包含第一组中的任何类,并且具有类型严格的父项和子项。

首先,让我们声明第一组相互继承的类。暂时忽略这Node一点。

public class BaseClass 
{
    public Node ContainerNode { get; set; }
}

public class DerivedClass1 : BaseClass
{
}

public class DerivedClass2 : BaseClass
{
}

这些类不能做太多,但这只是一个例子。

现在让我们设置另一组可以参与树的类。树中的每个元素都称为 a 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 层。好吧,他们没有,因为我们设置了一个隐式转换:

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

DotNetFiddle 上的完整工作代码

于 2017-11-14T05:27:45.473 回答