为何C# 11编译器拒绝可赋值的泛型参数作为泛型类型实参?
泛型约束中的协变转换矛盾解析
先看简化后的代码场景:
public interface IAnimal { } public interface IAnimalOperationResult<out TAnimal> where TAnimal : IAnimal { } public record DefaultSuccessfulResult<TAnimal>() : IAnimalOperationResult<TAnimal> where TAnimal : IAnimal; public abstract class AnimalHandler<TAnimal, TSuccessfulAnimalOperationResult> where TAnimal : IAnimal where TSuccessfulAnimalOperationResult : IAnimalOperationResult<IAnimal> { } // 编译器报错CS0311:不存在从DefaultSuccessfulResult<TAnimal>到IAnimalOperationResult<IAnimal>的隐式转换 public class AnimalHandlerWithDefaultSuccessfulResult<TAnimal> : AnimalHandler<TAnimal, DefaultSuccessfulResult<TAnimal>> where TAnimal : IAnimal { }
但下面的代码却能正常编译,说明转换逻辑上是成立的:
public record Dog() : IAnimal; [Fact] public void CanAssignValues() { DefaultSuccessfulResult<Dog> source = new(); // 此处直接赋值成功,说明转换存在 IAnimalOperationResult<IAnimal> target = source; }
问题核心:开放构造类型 vs 封闭构造类型
编译器在检查泛型类的继承约束时,处理的是开放构造类型——也就是TAnimal还只是一个未绑定的类型参数,虽然它有where TAnimal : IAnimal的约束,但编译器不会在约束检查阶段推导协变转换。
具体来说:
- 对于
AnimalHandlerWithDefaultSuccessfulResult<TAnimal>,基类要求TSuccessfulAnimalOperationResult必须直接实现IAnimalOperationResult<IAnimal>,但DefaultSuccessfulResult<TAnimal>实现的是IAnimalOperationResult<TAnimal>。此时TAnimal是一个变量,编译器不会主动将IAnimalOperationResult<TAnimal>协变转换为IAnimalOperationResult<IAnimal>来满足约束——约束检查只看直接的类型实现关系,不做协变推导。 - 而在
CanAssignValues方法中,DefaultSuccessfulResult<Dog>是封闭构造类型,Dog是具体的IAnimal实现类,此时编译器可以明确应用协变规则:因为IAnimalOperationResult<out T>是协变接口,所以IAnimalOperationResult<Dog>可以隐式转换为IAnimalOperationResult<IAnimal>。
解决方法
调整基类AnimalHandler的约束,让TSuccessfulAnimalOperationResult的约束与TAnimal关联,而不是直接绑定到IAnimal:
public abstract class AnimalHandler<TAnimal, TSuccessfulAnimalOperationResult> where TAnimal : IAnimal where TSuccessfulAnimalOperationResult : IAnimalOperationResult<TAnimal> { }
这样DefaultSuccessfulResult<TAnimal>就能直接满足约束,同时如果需要将TSuccessfulAnimalOperationResult转换为IAnimalOperationResult<IAnimal>,可以在基类内部利用协变特性完成转换,因为TAnimal是IAnimal的子类型,协变接口允许这种转换。
内容的提问来源于stack exchange,提问作者Rodolfo Grave
相关产品推荐
相关产品推荐

