泛型参数为自身泛型类的类设计是否合理?
问题描述
最初实现了一个前缀搜索树类:
public class PrefixTree<TData> { private PrefixTree<TData>[] _children; private void SomeMethod() { _children = new PrefixTree<TData>[10]; } }
随后创建了具备额外节点特性的派生类:
public class NewPrefixTree<TData> : PrefixTree<TData>
问题在于派生类的SomeMethod()仍会创建基类实例,不符合预期。
之后将基类重构为:
public abstract class PrefixTree<TData, TNode> where TNode : PrefixTree<TData, TNode>, new() { private TNode[] _children; private void SomeMethod() { _children = new TNode[10]; } }
尽管基类功能完整,但不得不设为abstract,因为无法直接实例化自身泛型参数的类。
现在可按如下方式使用且运行正常:
public class NewPrefixTree<TData> : PrefixTree<TData, NewPrefixTree<TData>> {} // 派生类使用 //或 public class PrefixTree<TData> : PrefixTree<TData, PrefixTree<TData>> {} // 使用基类功能
想请教这种将自身泛型类作为泛型参数的设计是否合理?还是需要借助泛型接口的协变/逆变特性(但我同时将类类型用作方法参数和返回类型,可能不适用)?
设计合理性分析与建议
这种递归泛型约束的设计是合理的,属于.NET生态里常用的「奇异递归模板模式(CRTP)」实现,专门解决你遇到的「基类需要创建派生类实例」的场景。
设计合理性说明
- 它精准解决了核心问题:让基类方法能创建正确的派生类实例,而非固定的基类实例,同时保留了基类的通用逻辑复用能力。
- 泛型约束
where TNode : PrefixTree<TData, TNode>, new()既保证了TNode必须是当前基类的派生类,又确保其具备无参构造函数,既满足了new TNode[10]的实例化要求,也保证了类型安全。 - 基类设为
abstract是合理的:因为它的设计定位是作为模板供派生类继承,本身不应该被直接实例化(直接实例化会陷入泛型参数的循环依赖)。
协变/逆变的适用性判断
你提到的泛型接口协变/逆变确实不适合当前场景:
- 协变(
out)要求类型只能作为返回值,逆变(in)要求只能作为输入参数,但你的场景中TNode既用于实例化数组,又可能在后续逻辑中同时作为方法参数和返回值,不符合协变/逆变的使用条件。 - 协变/逆变的核心作用是解决类型转换的兼容性问题,而非「基类创建派生类实例」的需求,应用方向不匹配。
额外优化建议
如果想让基础前缀树的使用更简洁,可以在基类所在程序集中提供一个默认实现,避免使用者重复编写包装类:
public class DefaultPrefixTree<TData> : PrefixTree<TData, DefaultPrefixTree<TData>> {}
这样外部直接使用DefaultPrefixTree<TData>就能获得基础前缀树功能,无需自行实现继承逻辑。
内容的提问来源于stack exchange,提问作者Max S
相关产品推荐
相关产品推荐

