所以,让我说我有:
Public Interface ISomeInterface
End Interface
Public Class SomeClass
Implements ISomeInterface
End Class
如果MyList
为List(Of SomeClass)
,我无法直接设置List(Of ISomeInterface) = MyList
。但是,我可以设置IEnumerable(Of ISomeInterface) = MyList
。
由于我对Covariance的理解,我认为它应该列出工作列表,因为List(Of T)
实现了IEnumerable(Of T)
。显然,我错过了一些东西。
为什么这样工作?具体为什么我不能做类似的事情:
Dim Animals As new List(Of Animal)
Dim Cats As List(Of IAnimal) = Animals
Animal实现IAnimal接口。但我能做到:
Dim Animals As New List(Of Animal)
Dim Cats As IEnumerable(Of IAnimal) = Animals
答案 0 :(得分:5)
我记得以前在网上看过很多关于这个问题的信息,所以我不确定我的回答是否真的会添加任何新内容,但我会尝试。
如果你正在使用.NET 4,那么请注意IEnumerable(Of T)的定义实际上是IEnumerable(Of Out T)。版本4中引入了新的Out关键字,表示此接口的协方差。然而,List(Of T)类被简单地定义为List(Of T)。这里不使用Out关键字,因此该类不是协变的。
我将提供一些示例来尝试解释为什么某些作业(例如您正在描述的作业)无法完成。我看到你的问题是用VB编写的,所以我很抱歉使用C#。
假设您有以下类:
abstract class Vehicle
{
public abstract void Travel();
}
class Car : Vehicle
{
public override void Travel()
{
// specific implementation for Car
}
}
class Plane : Vehicle
{
public override void Travel()
{
// specific implementation for Plane
}
}
您可以创建汽车列表,其中只能包含从汽车派生的对象:
List<Car> cars = new List<Car>();
您还可以创建一个平面列表,该列表只能包含从平面派生的对象:
List<Plane> planes = new List<Plane>();
您甚至可以创建一个车辆列表,其中可以包含从Vehicle派生的任何对象:
List<Vehicle> vehicles = new List<Vehicle>();
将汽车添加到汽车列表是合法的,将飞机添加到飞机列表是合法的。将汽车和飞机添加到车辆列表中也是合法的。因此,以下所有代码行都是有效的:
cars.Add(new Car()); // add a car to the list of cars
planes.Add(new Plane()); // add a plane to the list of planes
vehicles.Add(new Plane()); // add a plane to the list of vehicles
vehicles.Add(new Car()); // add a car to the list of vehicles
将汽车添加到飞机列表中是不合法的,将飞机添加到汽车列表也不合法。以下代码行不会编译:
cars.Add(new Plane()); // can't add a plane to the list of cars
planes.Add(new Car()); // can't add a car to the list of planes
因此,通过将汽车列表或飞机列表分配给车辆变量来尝试绕过此限制是不合法的:
vehicles = cars; // This is not allowed
vehicles.Add(new Plane()); // because then you could do this
考虑上面两行代码的含义。它说车辆变量实际上是一个List<Car>
对象,它应该只包含从Car派生的对象。但是,因为List<Vehicle>
包含Add(Vehicle)方法,理论上可以将Plane对象添加到List<Car>
集合中,这绝对不正确。
但是,将汽车列表或飞机列表分配到IEnumerable<Vehicle>
变量是完全有效的。
IEnumerable<Vehicle> vehicles = cars;
foreach (Vehicle vehicle in vehicles)
{
vehicle.Travel();
}
这里的快速解释是IEnumerable接口不允许您操作集合。它本质上是一个只读接口。 T对象(在这种情况下为Vehicle)仅作为IEnumerable接口的Current属性的返回值公开。没有将Vehicle对象作为输入参数的方法,因此不存在以非法方式修改集合的危险。
备注:我一直认为IList<T>
接口是IReadableList<out T>
接口和IWritableList<in T>
接口的组合是有意义的。
答案 1 :(得分:2)
在将List(Of SomeClass)
变量分配给List(Of ISomeInterface)
后,考虑一下ISomeInterface
可以做些什么。
您可以添加任何实现SomeOtherClass
的对象,例如List(Of SomeClass)
,并且不再拥有有效的List(Of T)
这就是在这种情况下没有为{{1}}定义协方差
的原因