因此,假设我有:
Public Interface ISomeInterface
End Interface
Public Class SomeClass
Implements ISomeInterface
End Class
Run Code Online (Sandbox Code Playgroud)
如果我有MyListas List(Of SomeClass),则无法直接设置List(Of ISomeInterface) = MyList。但是,我可以设置一个IEnumerable(Of ISomeInterface) = MyList。
基于对协方差的理解,我认为自List(Of T)实施以来,它应该在列表中起作用IEnumerable(Of T)。显然,我缺少了一些东西。
为什么这样工作?具体说明为什么我不能做这样的事情:
Dim Animals As new List(Of Animal)
Dim Cats As List(Of IAnimal) = Animals
Run Code Online (Sandbox Code Playgroud)
Animal实现IAnimal接口的位置。但我可以:
Dim Animals As New List(Of Animal)
Dim Cats As IEnumerable(Of IAnimal) = Animals
Run Code Online (Sandbox Code Playgroud)
我记得以前在网络上看到过很多有关此问题的信息,因此我不确定我的答案是否会真正添加新内容,但我会尽力而为。
如果使用的是.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
}
}
Run Code Online (Sandbox Code Playgroud)
您可以创建汽车列表,其中只能包含从Car派生的对象:
List<Car> cars = new List<Car>();
Run Code Online (Sandbox Code Playgroud)
您还可以创建一个平面列表,其中只能包含从“平面”派生的对象:
List<Plane> planes = new List<Plane>();
Run Code Online (Sandbox Code Playgroud)
您甚至可以创建一个Vehicles列表,其中可以包含从Vehicle派生的任何对象:
List<Vehicle> vehicles = new List<Vehicle>();
Run Code Online (Sandbox Code Playgroud)
将汽车添加到汽车列表中是合法的,并且将飞机添加到飞机列表中是合法的。将汽车和飞机都添加到车辆列表中也是合法的。因此,以下所有代码行均有效:
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
Run Code Online (Sandbox Code Playgroud)
将飞机添加到飞机列表中是不合法的,将飞机添加到汽车列表中也是不合法的。以下代码行将无法编译:
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
Run Code Online (Sandbox Code Playgroud)
因此,尝试通过为车辆变量分配汽车列表或飞机列表来绕过此限制是不合法的:
vehicles = cars; // This is not allowed
vehicles.Add(new Plane()); // because then you could do this
Run Code Online (Sandbox Code Playgroud)
考虑上面两行代码在说什么。就是说,Vehicles变量实际上是一个List<Car>对象,只应包含从Car派生的对象。但是,由于List<Vehicle>包含Add(Vehicle)方法,从理论上讲可以将Plane对象添加到List<Car>集合中,这绝对是不正确的。
但是,将汽车列表或飞机列表分配给IEnumerable<Vehicle>变量是完全有效的。
IEnumerable<Vehicle> vehicles = cars;
foreach (Vehicle vehicle in vehicles)
{
vehicle.Travel();
}
Run Code Online (Sandbox Code Playgroud)
这里的快速解释是IEnumerable接口不允许您操纵集合。它本质上是一个只读接口。T对象(在这种情况下为车辆)仅在IEnumerable接口的Current属性上作为返回值公开。没有将“车辆”对象作为输入参数的方法,因此不存在以非法方式修改集合的危险。
旁注:我一直认为IList<T>接口是IReadableList<out T>接口和IWritableList<in T>接口的组合是有意义的。