C# 将Move<Person>赋值给IMove<IPerson>报隐式转换错误原因求解
错误产生原因
C# 中的泛型类型参数默认是不变的:即使 Person 实现了 IPerson 接口,IMove<Person> 和 IMove<IPerson> 两种类型默认也没有兼容的隐式转换关系,无法直接互相赋值。
该限制是出于类型安全的设计,核心逻辑如下:
你定义的 IMove<T> 接口中,泛型参数 T 是作为 ChangeLocation 方法的输入参数使用的。如果允许 IMove<IPerson> t3 = new Move<Person>(); 编译通过,后续运行时会出现类型冲突:
假设你新增了另一个实现
IPerson接口的类Student,那么通过IMove<IPerson>类型的变量t3调用t3.ChangeLocation(new Student())是完全符合语法规则的——毕竟Student是合法的IPerson实现类。但t3实际指向的是Move<Person>类的实例,Move<Person>的ChangeLocation方法只能接收Person类型的参数,传入Student实例就会出现类型不匹配的错误。
编译器为了提前规避这种运行时类型错误,直接在编译阶段就禁止了这种不合法的转换。
拓展:泛型协变/逆变规则
如果你需要在泛型接口上实现不同类型参数的转换,可以通过 out/in 关键字标记泛型参数的方差:
- 若泛型参数仅作为输出(方法返回值、只读属性值等)使用,可以用
out标记为协变,此时如果T是U的子类/实现类,IFoo<T>可以隐式转换为IFoo<U>,例如 .NET 内置的IEnumerable<out T>就支持该特性 - 若泛型参数仅作为输入使用,可以用
in标记为逆变,此时如果T是U的子类/实现类,IFoo<U>可以隐式转换为IFoo<T>,例如 .NET 内置的IComparer<in T>就支持该特性
你的 IMove<T> 中 T 是方法入参,最多只能标记为逆变,支持的转换方向为 IMove<Person> t = new Move<IPerson>();,和你尝试的赋值方向完全相反,所以无论如何你的写法都不符合类型安全要求。
内容的提问来源于stack exchange,提问作者surzyn

