如何在未知泛型参数具体类型时转换为C#泛型接口?
解决泛型接口实例转换异常及避免手动类型转换的方案
问题根源
C# 默认的泛型接口是不变的——即便ConcreteModel继承自IBaseModelType,IFooGenericObjectProvider<ConcreteModel>和IFooGenericObjectProvider<IBaseModelType>也被视为完全独立的类型,直接转换必然抛出异常。
下面根据接口的使用场景,给出两种无需手动转换类型的解决方案:
方案一:使用协变泛型接口(适用于TModel仅作为输出的场景)
如果你的接口中,TModel仅作为方法返回值、只读属性等输出类型使用,可以给泛型接口添加out关键字标记为协变,这样就能实现子类型到基类型的接口转换。
步骤1:修改泛型接口为协变
// 添加out关键字,约束TModel继承IBaseModelType public interface IFooGenericObjectProvider<out TModel> where TModel : IBaseModelType { // 仅包含返回TModel的方法,不能有接收TModel作为参数的方法 TModel GetObject(); IEnumerable<TModel> GetObjects(); }
步骤2:实现类正常基于具体模型实现
public class SomeObjectProvider : IFooGenericObjectProvider<ConcreteModel> { public ConcreteModel GetObject() { return new ConcreteModel(); } public IEnumerable<ConcreteModel> GetObjects() { yield return new ConcreteModel(); } }
步骤3:反射创建实例后直接转换
var providerType = typeof(SomeObjectProvider); var instance = Activator.CreateInstance(providerType); // 此时转换不会抛出异常 var genericProvider = (IFooGenericObjectProvider<IBaseModelType>)instance; // 调用方法时,返回的是IBaseModelType类型,实际实例是ConcreteModel IBaseModelType model = genericProvider.GetObject();
方案二:拆分接口+泛型方法调用(适用于TModel作为输入参数的场景)
如果接口需要包含接收TModel作为输入参数的方法(比如UpdateObject(TModel model)),协变就无法使用。这时可以通过拆分接口+泛型方法的方式,避免手动转换类型。
步骤1:定义非泛型基础接口
先提取通用操作到非泛型接口,用于反射后的基础调用:
public interface IFooObjectProvider { // 通用方法,比如初始化、配置等与具体模型无关的操作 void Initialize(); }
步骤2:泛型接口继承非泛型接口
public interface IFooGenericObjectProvider<TModel> : IFooObjectProvider where TModel : IBaseModelType { // 既可以有返回TModel的方法,也可以有接收TModel的方法 TModel GetObject(); void UpdateObject(TModel model); }
步骤3:实现类正常实现泛型接口
public class SomeObjectProvider : IFooGenericObjectProvider<ConcreteModel> { public void Initialize() { // 初始化逻辑 } public ConcreteModel GetObject() { return new ConcreteModel(); } public void UpdateObject(ConcreteModel model) { // 更新逻辑,直接使用ConcreteModel类型,无需转换 } }
步骤4:反射创建实例后用泛型方法处理
通过泛型方法封装类型相关操作,避免手动转换:
// 定义泛型处理方法,直接使用具体TModel类型 public static void ProcessModelProvider<TModel>(IFooGenericObjectProvider<TModel> provider) where TModel : IBaseModelType { provider.Initialize(); TModel model = provider.GetObject(); provider.UpdateObject(model); // 所有操作都基于具体TModel类型,无需手动转换 } // 反射创建实例后调用泛型方法 var providerType = typeof(SomeObjectProvider); var instance = Activator.CreateInstance(providerType); // 方式1:用dynamic绕过编译时类型检查(简单但无编译检查) ProcessModelProvider((dynamic)instance); // 方式2:反射调用泛型方法(安全但代码稍繁琐) var processMethod = typeof(YourCurrentClass).GetMethod("ProcessModelProvider") .MakeGenericMethod(typeof(ConcreteModel)); processMethod.Invoke(null, new[] { instance });
额外小技巧:提前知晓具体类型时直接创建强类型实例
如果在反射前已经知道目标实现类对应的具体TModel类型,可以直接创建强类型接口实例,完全跳过转换步骤:
// 直接转换为具体类型的泛型接口 var provider = Activator.CreateInstance(typeof(SomeObjectProvider)) as IFooGenericObjectProvider<ConcreteModel>; // 直接调用方法,参数和返回值都是ConcreteModel类型 ConcreteModel model = provider.GetObject(); provider.UpdateObject(model);
内容的提问来源于stack exchange,提问作者DGibbs
相关产品推荐
相关产品推荐

