马克是对的;只是为了给你一些关于为什么这行不通的更多背景知识。考虑对您的代码进行以下重命名:
interface IZoo<TCage, TAnimal>
where TCage : ICage<TAnimal>
where TAnimal : IAnimal
{
}
class Zoo<TCage, TAnimal> : IZoo<TCage, TAnimal>
where TCage : ICage<TAnimal>
where TAnimal : IAnimal
{
}
interface ICage<TAnimal> where TAnimal : IAnimal
{
}
interface IAnimal
{
}
class FishTank<TAnimal> : ICage<TAnimal> where TAnimal : IAnimal
{
}
class Fish : IAnimal
{
}
现在你的问题是,为什么这不合法:
Zoo<FishTank<Fish>, Fish> aquarium = new Zoo<FishTank<Fish>, Fish>();
IZoo<ICage<IAnimal>, IAnimal> zoo = aquarium;
?
因为假设现在 IZoo 上有一个方法:
interface IZoo<TCage, TAnimal>
where TCage : ICage<TAnimal>
where TAnimal : IAnimal
{
void PutAnimalInCage(TCage cage, TAnimal animal);
}
然后你说:
zoo.PutAnimalInCage(giraffePaddock, giraffe);
而你只是把一个长颈鹿围场放进了一个水族馆!在您想要的转换是合法的并且 IZoo 可以有您选择的任何方法的世界中,我们无法维护类型安全。
现在,这只是危险的,因为 IZoo 有这样的方法。如果它没有这样的方法,那么你是对的,那可能是完全安全的。在 C# 4.0 中,我们向该语言添加了一项功能,以便您可以通过使用“out”注释您想要协变的类型参数以及您想要的参数来询问编译器“检查该接口是否可以安全地变体”与“in”逆变。如果您这样做,那么编译器将检查您是否可以使您想要的方差成为类型安全的。如果不能,则不允许声明类型。
这个问题通常出现在 StackOverflow 上的方式是人们问为什么这是非法的:
List<Giraffe> giraffes = new List<Giraffe>();
List<Mammal> mammals = giraffes; // illegal
同样的原因。因为以后没有什么能阻止你了
mammals.Add(new Tiger());
您刚刚在长颈鹿列表中添加了一只老虎。相同的推理,只是一个更简单的案例。