C#:如何将泛型参数限制为调用者静态类型?实现流畅WithValue方法
实现基类内部的类型安全不可变Setter方法(WithValue)
核心思路
C#没有直接支持where T : this的约束,但可以通过**递归泛型(CRTP,Curiously Recurring Template Pattern)**在基类内部实现类型安全的流畅不可变Setter,让WithXXX方法返回调用者的静态类型,无需显式指定泛型参数。
方案一:自引用泛型基类+子类实例化委托
这是最常用且可控的实现方式,让基类通过泛型参数感知子类类型,同时将实例化逻辑委托给子类,保证类型正确性。
代码示例
// 抽象基类,使用自引用泛型约束 public abstract class StrategyBase<T> where T : StrategyBase<T> { // 公共属性(不可变,仅通过构造函数初始化) public int Priority { get; } protected StrategyBase(int priority) { Priority = priority; } // 可复用的With方法,返回子类静态类型 public T WithPriority(int newPriority) { // 调用子类实现的实例创建逻辑 return CreateNewInstance(newPriority); } // 抽象方法,强制子类负责创建自身的新实例 protected abstract T CreateNewInstance(int newPriority); } // 子类继承时指定自身为泛型参数 public class DiscountStrategy : StrategyBase<DiscountStrategy> { // 子类专属属性 public decimal DiscountRate { get; } public DiscountStrategy(int priority, decimal discountRate) : base(priority) { DiscountRate = discountRate; } protected override DiscountStrategy CreateNewInstance(int newPriority) { // 创建新的子类实例:保留原有专属属性,替换公共属性 return new DiscountStrategy(newPriority, DiscountRate); } // 子类自定义的With方法,同样遵循流畅语法 public DiscountStrategy WithDiscountRate(decimal newRate) { return new DiscountStrategy(Priority, newRate); } }
使用方式
调用时完全无需指定泛型,自动返回子类类型,支持链式调用:
var strategy = new DiscountStrategy(2, 0.1m) .WithPriority(1) .WithDiscountRate(0.15m); // strategy的静态类型是DiscountStrategy,无需强制转换
优缺点
- ✅ 完全在基类内部实现机制,无需扩展方法
- ✅ 类型安全,编译时即可校验返回类型
- ✅ 流畅语法自然,符合不可变对象的设计习惯
- ⚠️ 子类必须正确指定自身为泛型参数(写错会导致编译错误,容易发现)
方案二:基类配合虚拟方法+类型转换(不推荐)
如果不想用泛型基类,也可以在基类定义虚拟方法,子类重写时返回自身类型,但需要显式转换,存在运行时类型转换风险,仅适合简单场景:
public abstract class StrategyBase { public int Priority { get; } protected StrategyBase(int priority) { Priority = priority; } public virtual StrategyBase WithPriority(int newPriority) { throw new NotImplementedException("子类必须重写此方法"); } } public class DiscountStrategy : StrategyBase { public decimal DiscountRate { get; } public DiscountStrategy(int priority, decimal discountRate) : base(priority) { DiscountRate = discountRate; } // 重写时返回子类类型,覆盖基类返回值 public new DiscountStrategy WithPriority(int newPriority) { return new DiscountStrategy(newPriority, DiscountRate); } }
优缺点
- ✅ 基类无需泛型
- ⚠️ 基类方法无法强制子类返回正确类型,依赖子类重写时的
new关键字 - ⚠️ 如果通过基类引用调用方法,返回的还是基类类型,会破坏流畅性
总结
优先选择方案一(CRTP自引用泛型),它是实现基类内部类型安全不可变Setter的最优解,完全满足你要求的:无需显式泛型、返回调用者静态类型、基类内部封装可复用逻辑的需求。
内容的提问来源于stack exchange,提问作者andreas
相关产品推荐
相关产品推荐

