Я помню, что видел много информации об этой проблеме в Интернете ранее, поэтому я не уверен, что мой ответ действительно добавит что-то новое, но я попробую.
Если вы используете .NET 4, то обратите внимание, что определение IEnumerable (Of T) на самом деле IEnumerable (Of Out T). Новое ключевое слово Out было введено в версии 4, что указывает на ковариацию этого интерфейса. Класс 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>();
Вы даже можете создать список Транспортных средств, который может содержать любой объект, полученный из Транспортного средства:
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
Посмотрите, что две строки кода говорят выше. Это говорит о том, что переменная Vehicles на самом деле является 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 (Транспортные средства в данном случае) отображаются только как возвращаемое значение в свойстве Current интерфейса IEnumerable. Нет методов, принимающих объекты Vehicle в качестве входных параметров, поэтому нет опасности, что коллекция будет изменена незаконным образом.
Примечание: я всегда думал, что было бы целесообразно, чтобы интерфейс IList<T>
был составным из интерфейса IReadableList<out T>
и интерфейса IWritableList<in T>
.