gpt4 book ai didi

.net - List 与 IEnumerable 的协变和逆变

转载 作者:行者123 更新时间:2023-12-01 09:35:37 24 4
gpt4 key购买 nike

所以,假设我有:

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 接口(interface)的地方。但我可以:

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 关键字,表示该接口(interface)的协方差。然而,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>();

您还可以创建平面列表,其中只能包含从平面派生的对象:

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

您甚至可以创建一个 Vehicles 列表,其中可以包含任何从 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 接口(interface)不允许您操作集合。它本质上是一个只读接口(interface)。 T 对象(在本例中为车辆)仅作为 IEnumerable 接口(interface)的 Current 属性的返回值公开。没有将 Vehicle 对象作为输入参数的方法,因此不存在非法修改集合的危险。

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

关于.net - List 与 IEnumerable 的协变和逆变,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8565732/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com