C#泛型类赋值报错:如何声明存储AbstractControler子类的变量?
解决C#泛型抽象类子类无法赋值给基类泛型类型的问题
你遇到的核心问题是C#泛型默认是不变类型:即便Squadron实现了IModelWithId,AbstractControler<Squadron>也不能直接转换为AbstractControler<IModelWithId>——因为你的泛型类同时涉及T的输入(比如接收T作为参数的方法)和输出(比如返回T的方法),无法通过单纯的协变(out T)或逆变(in T)接口解决。
下面提供两种可行的实现方向:
方案一:定义非泛型基接口(最通用的解决方案)
通过一个非泛型的接口作为所有控制器的统一父类型,把不需要泛型的公共方法放在这个非泛型类型中,泛型特有的方法保留在泛型抽象类里。
步骤1:创建非泛型接口
public interface IAbstractControler { // 定义所有控制器通用的非泛型方法,例如: void ExecuteCommonLogic(); int GetModelId(object model); // 利用IModelWithId的特性做兼容 }
步骤2:修改泛型抽象类实现该接口
public abstract class AbstractControler<T> : IAbstractControler where T : IModelWithId { // 实现非泛型接口的方法 public void ExecuteCommonLogic() { // 通用逻辑实现,或声明为abstract让子类重写 } public int GetModelId(object model) { if (model is T typedModel) return typedModel.Id; throw new ArgumentException("传入模型类型与控制器泛型类型不匹配"); } // 泛型特有的业务方法(保留原有逻辑) public abstract T GetModelById(int id); public abstract void UpdateModel(T model); }
步骤3:统一存储与赋值
第三个类的属性声明为非泛型接口类型:
internal IAbstractControler Controler { get; set; }
此时赋值完全正常:
Controler = SquadronsControler.getInstance();
如果需要调用泛型特有的方法,可以在使用时做类型检查和转换:
if (Controler is AbstractControler<Squadron> squadronControler) { var targetSquadron = squadronControler.GetModelById(1001); squadronControler.UpdateModel(targetSquadron); }
方案二:拆分协变/逆变接口(适合读写分离场景)
如果你的业务可以拆分为“只读”和“只写”操作,可以分别定义协变和逆变接口,让泛型抽象类同时实现这两个接口,根据场景选择对应的接口类型存储。
步骤1:拆分协变/逆变接口
// 协变接口:只输出T(方法返回值为T),用out修饰 public interface IReadOnlyControler<out T> where T : IModelWithId { T GetModelById(int id); } // 逆变接口:只输入T(方法参数为T),用in修饰 public interface IWriteableControler<in T> where T : IModelWithId { void UpdateModel(T model); }
步骤2:泛型抽象类实现双接口
public abstract class AbstractControler<T> : IReadOnlyControler<T>, IWriteableControler<T> where T : IModelWithId { public abstract T GetModelById(int id); public abstract void UpdateModel(T model); }
步骤3:根据场景选择接口存储
- 如果只需要读取操作,可以用协变接口存储:
internal IReadOnlyControler<IModelWithId> ReadOnlyControler { get; set; } // 赋值正常,协变支持子类泛型向父类泛型转换 ReadOnlyControler = SquadronsControler.getInstance(); // 调用返回IModelWithId类型 var model = ReadOnlyControler.GetModelById(1001); - 如果需要写入操作,则只能针对具体泛型类型声明(无法统一为
IModelWithId,因为逆变只支持父类泛型向子类泛型转换):internal IWriteableControler<Squadron> WriteableControler { get; set; } WriteableControler = SquadronsControler.getInstance();
为什么原有方案不行?
C#泛型类默认是不变的,这意味着AbstractControler<Squadron>和AbstractControler<IModelWithId>是完全独立的类型,编译器不允许直接转换——因为如果允许,你可能会把一个OtherModel : IModelWithId实例传给UpdateModel(T)方法,而该方法实际只接受Squadron类型,会导致运行时错误。协变/逆变接口通过限制T的使用方向(只能输出或只能输入)解决了这个安全问题,但你的场景需要T同时作为输入和输出,因此只能通过非泛型基类型来统一存储。
内容的提问来源于stack exchange,提问作者Targol
相关产品推荐
相关产品推荐

