C#泛型接口逆变转换失败求助:派生类转ICommand<IOrderCommand>
泛型逆变转换失败问题解决
在依赖注入场景中,尝试将实现了ICommand<CreateOrderCommand>的具体类对象转换为ICommand<IOrderCommand>时抛出InvalidCastException,即使泛型接口标记了in(逆变)关键字也无法成功。简化代码如下:
public class OrderResult { } public interface IOrderCommand { } public interface ICommand<in T> where T : IOrderCommand { Task<OrderResult> HandleAsync(T orderCommand); } public class CreateOrderCommand : IOrderCommand { } public class Concreate : ICommand<CreateOrderCommand> { public Task<OrderResult> HandleAsync(CreateOrderCommand command) { throw new NotImplementedException(); } }
执行转换时的代码及异常:
var concreateType = new Concreate(); var command = (ICommand<IOrderCommand>)concreateType; // 转换失败
System.InvalidCastException: 'Unable to cast object of type 'Concreate' to type 'ICommand`1[IOrderCommand]'.'
原因分析
- 逆变方向搞反:
in关键字定义的逆变,允许将基类型参数的泛型接口实例转换为派生类型参数的泛型接口实例(例如ICommand<IOrderCommand>→ICommand<CreateOrderCommand>),而你尝试的是反向转换(ICommand<CreateOrderCommand>→ICommand<IOrderCommand>),不符合逆变规则。 - 类型安全限制:具体实现
Concreate的HandleAsync仅接受CreateOrderCommand类型,如果允许转换为ICommand<IOrderCommand>,则可能传入其他IOrderCommand派生类,导致运行时类型错误,CLR因此禁止该转换。
解决方案
方案1:定义非泛型基接口
新增非泛型ICommand接口作为泛型接口的基类,统一调用入口并做类型校验:
public interface ICommand { Task<OrderResult> HandleAsync(IOrderCommand orderCommand); } public interface ICommand<in T> : ICommand where T : IOrderCommand { Task<OrderResult> HandleAsync(T orderCommand); } // 实现类显式实现非泛型接口 public class Concreate : ICommand<CreateOrderCommand> { public Task<OrderResult> HandleAsync(CreateOrderCommand command) { // 具体业务逻辑 throw new NotImplementedException(); } Task<OrderResult> ICommand.HandleAsync(IOrderCommand orderCommand) { if (orderCommand is not CreateOrderCommand command) throw new ArgumentException("命令类型不匹配", nameof(orderCommand)); return HandleAsync(command); } }
使用时直接转换为非泛型接口:
var concreateType = new Concreate(); var command = (ICommand)concreateType; await command.HandleAsync(new CreateOrderCommand());
方案2:使用动态类型调用
若不需要编译时类型检查,可通过dynamic绕过类型限制(注意运行时类型错误风险):
var concreateType = new Concreate(); dynamic command = concreateType; await command.HandleAsync((IOrderCommand)new CreateOrderCommand());
方案3:反射调用
通过反射获取方法并执行,适合完全动态的场景:
var concreateType = new Concreate(); var method = concreateType.GetType().GetMethod(nameof(ICommand<CreateOrderCommand>.HandleAsync)); var result = (Task<OrderResult>)method.Invoke(concreateType, new object[] { new CreateOrderCommand() }); await result;
内容的提问来源于stack exchange,提问作者Usman
相关产品推荐
相关产品推荐

