C#泛型接口赋值报错:无法将IAnimalLoader<Cat>转为IAnimalLoader<IAnimal>
为什么IAnimalLoader无法赋值给IAnimalLoader?
这个问题我之前也碰到过,核心原因是你的泛型接口没有启用协变支持——C#里泛型默认是「不变」的,哪怕Cat实现了IAnimal,IAnimalLoader<Cat>和IAnimalLoader<IAnimal>也不会自动成为兼容类型。
问题根源:泛型的不变性
默认情况下,对于泛型类型Generic<T>,即使TSub是TSuper的子类型,Generic<TSub>和Generic<TSuper>之间也没有任何继承或兼容关系。这是C#为了类型安全设计的:如果允许随意转换,可能会出现把错误类型的对象塞进泛型类型的风险(不过你的场景里不存在这个问题,因为你的接口只有输出操作)。
解决方案:给接口添加协变修饰符out
观察你的IAnimalLoader<T>接口,所有用到T的地方都是输出位置(方法的返回值):
IQueryable<T> GetAllQueryable();返回T的查询集合Task<T> GetAsync(string id);返回单个T对象
这种场景完全符合协变的使用条件,只需要在接口定义时给类型参数T加上out关键字,告诉C#这个接口支持协变转换:
public interface IAnimalLoader<out T> where T : IAnimal { IQueryable<T> GetAllQueryable(); Task<T> GetAsync(string id); }
添加out修饰符后,IAnimalLoader<Cat>就可以隐式转换为IAnimalLoader<IAnimal>了,你原来的赋值代码不需要任何强制转换就能正常运行:
if (animal == AnimalType.Cat) { animalLoader = _catLoader; // 现在不会报错 } else { animalLoader = _dogLoader; // 现在不会报错 }
注意事项
协变的关键限制是:类型参数T只能出现在输出位置(返回值),不能作为方法的输入参数。比如如果你的接口里有类似void Update(T animal)这样的方法(T作为输入),就不能使用out修饰符,此时需要考虑其他方案(比如泛型方法、抽象基类等)。
内容的提问来源于stack exchange,提问作者ArthClement
相关产品推荐
相关产品推荐

