考虑以下代码片段:
class Animal {}
class Dog : Animal {}
interface MyContainer<T> {
T Get(int i);
void Add (T thing);
}
class MyList<T> : MyContainer<T> {
private List<T> items = new List<T>();
public T Get(int i) { return items[i]; }
public void Add(T item) { items.Add(item); }
}
public static void Main(string[] args)
{
MyContainer<Dog> dogs = new MyList<Dog>();
// Why doesn't this cast dogs to animals?
MyContainer<Animal> animals = dogs;
animals.Add(new Animal());
Animal retrievedAnimal = animals.Get(0);
animals.Add(new Dog());
}
我知道上面的 C# 会失败,因为泛型是不变的。我知道
MyContainer<Animal> animals = dogs;
行将会失败,因为即使在您可能将 MyList<Dog>
称为 MyContainer<Animal>
(其中容器是协变的)的情况下,它仍然是 MyList<Dog>
并且误导性的引用可能会让用户感到困惑将 Cat
添加到 MyList<Dog>
,这就是为什么在这种情况下界面无法包含接受 T
. 的方法
我的问题是:当引用更改时,为什么编译器不简单地将
dogs
对象转换为 MyContainer<Animal>
?这对我来说是合乎逻辑的,从那时起,引用适当地指向正确类型的对象,我们可以自由地从容器中添加和删除任何Animal
。当我们使用引用来引用作为引用类型的子集但不等于它的对象时,似乎所有类型问题都会出现。编译器不可能隐式推断出这样的转换吗?
如果我正确地阅读了你的问题,那么你似乎对演员阵容的实际含义存在误解。您似乎认为强制转换实际上改变了对象的类型。事实并非如此。它只是通过不同类型的引用提供对同一对象的访问。 “cast”一词与“以不同的光线投射某物”的含义相同,即以不同的方式看待同一物体。你说:
从那时起,引用适当地指向正确类型的对象,我们可以自由地从容器中添加和删除任何动物
这需要实际对象从
Container<Dog>
更改为 Container<Animal>
,因为显然除了 Dog
之外你不能添加任何东西到 Container<Dog>
。您所说的是转换,而不是强制转换,这绝对不会隐式发生。既然同一个对象仍然可以被原始类型的另一个变量引用,那怎么可能呢?