C#中将Activator创建的实例转为泛型接口IBar<IFoo>的解决方案
问题根本原因
你遇到的是C#泛型不变性导致的运行时类型转换失败:
当前IBar<T>的泛型参数T没有声明变体修饰,且Bal方法将T作为输入参数,天然不支持将IBar<Foo>直接转换为IBar<IFoo>——因为转换后你可以传入任意实现了IFoo的类型实例到Bal方法,这会破坏Baz.Bal仅接受Foo类型的类型安全约束,CLR会在运行时禁止这个转换。你的代码能通过编译是因为编译器仅做了表层的继承关系校验,没有校验泛型变体的兼容性。
可行解决方案(无需反射,兼容.NET Framework 4.7.2 + C#7)
以下两个方案都可以满足你转接口、存List、调用Bal方法的需求:
方案1:新增非泛型基接口(最稳妥)
新增无泛型参数的IBar基接口,原有泛型接口继承该基接口,在抽象类中做统一的类型校验:
// 新增非泛型基接口 public interface IBar { void Bal(IFoo item); } // 原有泛型接口继承非泛型接口 public interface IBar<T> : IBar where T : IFoo { void Bal(T item); } public abstract class Bar<T> : IBar<T> where T : IFoo { public abstract void Bal(T item); // 显式实现非泛型接口的Bal方法,统一做类型校验 void IBar.Bal(IFoo item) { // C#7 写法,如果不支持is not可以替换为 if (!(item is T typedItem)) if (item is not T typedItem) { throw new ArgumentException($"参数必须为{typeof(T)}类型", nameof(item)); } Bal(typedItem); } }
修改后实例转换和存储逻辑如下:
// List 声明为 List<IBar> 即可 public IBar CreateInstanceOfBaz() { var instanceOfBaz = Activator.CreateInstance(typeof(Baz)); return (IBar)instanceOfBaz; }
调用时直接传入IFoo类型实例即可,类型校验逻辑统一封装在基类中,无需额外处理。
方案2:泛型包装类(保留IBar签名)
如果你必须保留IBar<IFoo>的接口签名、需要将实例存入List<IBar<IFoo>>,可以实现一个通用包装类做适配:
// 通用包装类,将IBar<T>适配为IBar<IFoo> public class BarWrapper<T> : IBar<IFoo> where T : IFoo { private readonly IBar<T> _innerBar; public BarWrapper(IBar<T> innerBar) { _innerBar = innerBar ?? throw new ArgumentNullException(nameof(innerBar)); } public void Bal(IFoo item) { if (item is not T typedItem) { throw new ArgumentException($"参数必须为{typeof(T)}类型", nameof(item)); } _innerBar.Bal(typedItem); } }
实例转换逻辑如下:
public IBar<IFoo> CreateInstanceOfBaz() { var instanceOfBaz = (IBar<Foo>)Activator.CreateInstance(typeof(Baz)); return new BarWrapper<Foo>(instanceOfBaz); }
该方案可以直接返回IBar<IFoo>类型,完全满足你存入对应泛型List的需求。
内容的提问来源于stack exchange,提问作者Roy Berris
相关产品推荐
相关产品推荐

