C#中如何将FieldInfo的值转换为泛型基类列表?
问题原因与解决方案
问题根源
List<SomeDerivedClass> 和 List<SomeSuperClass> 是完全独立的封闭泛型类型,C# 中 List<T> 属于不变类型——即便 SomeDerivedClass 继承自 SomeSuperClass,也无法直接将前者的实例转换为后者。只有支持协变的泛型接口(如 IEnumerable<out T>、IReadOnlyList<out T>)允许这种向上转换,因为这些接口的类型参数被标记为 out(仅用于输出场景)。
解决方法
根据业务需求,有几种可行的处理方式:
1. 转换为协变接口(推荐,无额外内存开销)
利用 List<T> 实现了 IEnumerable<out T> 的特性,先将集合转换为协变接口类型,再按需转为 List<SomeSuperClass>:
public virtual void SomeMethod() { FieldInfo[] fields = GetType().GetFields(); foreach (FieldInfo fieldInfo in fields) { if ((fieldInfo.FieldType.IsGenericType) && (fieldInfo.FieldType.GetGenericTypeDefinition() == typeof(List<>)) && (fieldInfo.FieldType.GetGenericArguments()[0].IsSubclassOf(typeof(SomeSuperClass)))) { // 获取派生类列表(断言已确认非空) var derivedClassList = fieldInfo.GetValue(this) as List<SomeDerivedClass>; Assert.IsNotNull(derivedClassList); // 直接转换为基类的IEnumerable(协变支持,无新对象创建) IEnumerable<SomeSuperClass> superclassEnumerable = derivedClassList; Assert.IsNotNull(superclassEnumerable); // 如果需要List<SomeSuperClass>实例,基于Enumerable创建新列表 List<SomeSuperClass> superclassList = superclassEnumerable.ToList(); Assert.IsNotNull(superclassList); } } }
2. 强制转换元素并生成新列表
如果需要直接得到 List<SomeSuperClass>,可以用 Cast<T> 方法转换集合中的元素:
var derivedList = fieldInfo.GetValue(this) as List<SomeDerivedClass>; List<SomeSuperClass> superclassList = derivedList.Cast<SomeSuperClass>().ToList();
3. 修改字段声明为协变接口(需权衡可写性)
如果业务允许限制集合的修改操作,可以将 Game 类中的字段声明为协变接口类型,这样就能直接转换为基类的接口实例:
public class Game : SomeSuperClass { // 用IReadOnlyList<T>声明,支持向上转换为IReadOnlyList<SomeSuperClass> public IReadOnlyList<SomeSuperClass> inventory_changes = new List<SomeDerivedClass>(); }
这种方式的缺点是无法通过接口直接添加/删除元素,仅适合只读场景。
内容的提问来源于stack exchange,提问作者SticksInGoo
相关产品推荐
相关产品推荐

