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

如何解决.NET Standard中重写方法不支持协变类型的问题?

解决.NET Standard中Builder模式协变返回类型不支持的问题

问题背景

实现了一套用于构建UI Schema的Builder模式,原本支持流畅的链式调用,但在.NET Standard环境下编译报错:Target runtime doesn't support covariant types in overrides。若将子类重写方法的返回类型改为基类UISchemaBuilderBase,则会导致链式调用中断(例如调用AddBoolean后无法直接调用EndGroup)。

原核心代码结构:

  • 抽象基类UISchemaBuilderBase包含AddBoolean等虚方法,返回基类类型
  • UISchemaBuilder继承基类,提供StartGroup方法返回UISchemaGroupBuilder
  • UISchemaGroupBuilder继承基类,重写AddBoolean返回自身类型,同时提供EndGroup返回UISchemaBuilder

解决方案:使用递归泛型(CRTP)实现类型安全链式调用

通过在基类中引入泛型参数约束自身子类,让基类方法返回具体子类类型,既兼容.NET Standard运行时要求,又能保留完整的链式调用能力。

修改后的代码实现

1. 泛型抽象基类

public abstract class UISchemaBuilderBase<TBuilder> where TBuilder : UISchemaBuilderBase<TBuilder>
{
    protected readonly UISchemaSpecification UiSchemaSpecification;

    protected UISchemaBuilderBase(UISchemaSpecification specification)
    {
        UiSchemaSpecification = specification;
    }

    public UISchemaSpecification Build() => UiSchemaSpecification;

    public virtual TBuilder AddBoolean(string fieldName, string groupName = null)
    {
        // 保留原有业务逻辑
        // ... some logic
        // UiSchemaSpecification.Properties[fieldName] = schema;
        return (TBuilder)this;
    }
}

2. 根Builder类

public class UISchemaBuilder : UISchemaBuilderBase<UISchemaBuilder>
{
    public UISchemaBuilder() : base(new UISchemaSpecification()) { }

    public UISchemaGroupBuilder StartGroup(string name)
    {
        UiSchemaSpecification.ParsecMetadata ??= new();
        UiSchemaSpecification.ParsecMetadata.Groups ??= new();
        UiSchemaSpecification.ParsecMetadata.Groups.Add(new(name));

        return new UISchemaGroupBuilder(name, this, UiSchemaSpecification);
    }
}

3. 分组Builder类

public class UISchemaGroupBuilder : UISchemaBuilderBase<UISchemaGroupBuilder>
{
    private readonly string _name;
    private readonly UISchemaBuilder _schemaBuilder;

    public UISchemaGroupBuilder(string name, UISchemaBuilder schemaBuilder, UISchemaSpecification specification)
        : base(specification)
    {
        _name = name;
        _schemaBuilder = schemaBuilder;
    }

    public override UISchemaGroupBuilder AddBoolean(string fieldName, string fieldTitle, string groupName = null)
    {
        base.AddBoolean(fieldName, _name);
        return this;
    }

    public UISchemaBuilder EndGroup() => _schemaBuilder;
}

验证链式调用

修改后的代码可正常支持原有的流畅调用逻辑,且完全兼容.NET Standard环境:

var schema = new UISchemaBuilder()
    .StartGroup("groupName")
        .AddBoolean("field1", "Field 1")
    .EndGroup()
    .StartGroup("anotherGroupName")
        .AddBoolean("field2", "Field 2")
    .EndGroup()
    .Build();

原理说明

递归泛型(CRTP,Curiously Recurring Template Pattern)让基类能够引用具体子类类型,使基类方法返回子类实例,避免了协变返回类型的使用,同时保证了链式调用的类型安全。这种方案适配.NET Standard的运行时限制,不会触发协变类型相关的编译错误。

内容的提问来源于stack exchange,提问作者Anthony

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 16:32:34