有没有办法声明抽象基础 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;
}
这可能看起来令人困惑和循环,但它确实可以编译。它基本上是说 parent
是 T
类型,它必须派生自泛型 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
如果我对你的要求没有理解错的话,你有一系列具有继承关系的类,你希望将它们排列成树状结构,其中每个实例都有一个相同类型的父实例,并且只有同一类型。这是一个有趣的问题。
稍微研究一下之后,我可以建议您将需求分成两个平行但相关的对象图,这样您就可以
- 一组类有继承关系
- 一组 类,可以包含第一组中的任何 类,并且具有类型严格的父项和子项。
首先,让我们声明第一组相互继承的类。暂时忽略 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.
假设我有一个抽象基 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;
}
这可能看起来令人困惑和循环,但它确实可以编译。它基本上是说 parent
是 T
类型,它必须派生自泛型 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
如果我对你的要求没有理解错的话,你有一系列具有继承关系的类,你希望将它们排列成树状结构,其中每个实例都有一个相同类型的父实例,并且只有同一类型。这是一个有趣的问题。
稍微研究一下之后,我可以建议您将需求分成两个平行但相关的对象图,这样您就可以
- 一组类有继承关系
- 一组 类,可以包含第一组中的任何 类,并且具有类型严格的父项和子项。
首先,让我们声明第一组相互继承的类。暂时忽略 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.