如何解决.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方法返回UISchemaGroupBuilderUISchemaGroupBuilder继承基类,重写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
相关产品推荐
相关产品推荐

