You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 21:55:11