协方差和逆变与列表与 IEnumerable



所以,假设我有:

Public Interface ISomeInterface
End Interface
Public Class SomeClass
  Implements ISomeInterface
End Class

如果我有MyList作为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

Animal 实现 IAnimal 接口的地方。 但我可以做到:

Dim Animals As New List(Of Animal)
Dim Cats As IEnumerable(Of IAnimal) = Animals

我记得以前在网上看到过很多关于这个问题的信息,所以我不确定我的答案是否真的会增加任何新内容,但我会尝试。

如果您使用的是 .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
    }
}

您可以创建一个汽车列表,该列表只能包含派生自 Car 的对象:

        List<Car> cars = new List<Car>();

您还可以创建平面列表,该列表只能包含派生自 Plane 的对象:

        List<Plane> planes = new List<Plane>();

您甚至可以创建一个载具列表,其中可以包含派生自载具的任何对象:

        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 对象(在本例中为 Vehicles(仅作为返回值公开在 IEnumerable 接口的 Current 属性上。 没有将 Vehicle 对象作为输入参数的方法,因此不存在以非法方式修改集合的危险。

旁注:我一直认为IList<T>接口是IReadableList<out T>接口和IWritableList<in T>接口的组合是有意义的。

考虑一下在

List(Of SomeClass)分配给List(Of ISomeInterface)变量后,您可以对它做什么可能会有所帮助。

您可以添加任何实现ISomeInterface的对象,例如 SomeOtherClass,并且不再具有有效的List(Of SomeClass)

这就是在这种情况下没有为List(Of T)定义协方差的原因

相关内容

  • 没有找到相关文章

最新更新