So, let's say I have:
Public Interface ISomeInterface
End Interface
Public Class SomeClass
Implements ISomeInterface
End Class
If I have MyList as List(Of SomeClass), I cannot directly set a List(Of ISomeInterface) = MyList. However, I can Set an IEnumerable(Of ISomeInterface) = MyList.
With my understanding of Covariance I thought that it should work list to list since List(Of T) implements IEnumerable(Of T). Clearly, I am missing something.
Why does it work that way? Specifically why Can't I do something like:
Dim Animals As new List(Of Animal)
Dim Cats As List(Of IAnimal) = Animals
Where Animal implements the IAnimal Interface. But I can do:
Dim Animals As New List(Of Animal)
Dim Cats As IEnumerable(Of IAnimal) = Animals
I recall seeing a lot of information around this issue on the web previously, so I'm not sure that my answer will really add anything new, but I'll try.
If you're using .NET 4, then notice that the definition of IEnumerable(Of T) is actually IEnumerable(Of Out T). The new Out keyword has been introduced in version 4, which indicates the covariance of this interface. The List(Of T) class, however, is simply defined as List(Of T). The Out keyword is not used here, so that class is not covariant.
I'll provide some examples to try to explain why certain assignments such as the one you're describing can't be done. I see that your question is written in VB, so my apologies for using C#.
Assume that you have the following classes:
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
}
}
You can create a list of cars, which can only contain objects derived from Car:
List<Car> cars = new List<Car>();
You can also create a list of planes, which can only contain objects derived from Plane:
List<Plane> planes = new List<Plane>();
You can even create a list of Vehicles, which can contain any object derived from Vehicle:
List<Vehicle> vehicles = new List<Vehicle>();
It's legal to add a car to the list of cars, and it's legal to add a plane to the list of planes. It's also legal to add both a car and a plane to the list of vehicles. Therefore, all of the following lines of code are valid:
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
It's not legal to add a car to the list of planes, nor is it legal to add a plane to the list of cars. The following lines of code won't compile:
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
Therefore, it's not legal to try to bypass this restriction by assigning the list of cars or the list of planes to the vehicles variable:
vehicles = cars; // This is not allowed
vehicles.Add(new Plane()); // because then you could do this
Consider what the two lines of code are saying above. It's saying that the vehicles variable is actually a List<Car> object, which should only contain objects derived from Car. However, because List<Vehicle> contains an Add(Vehicle) method, it would theoretically be possible to add a Plane object to the List<Car> collection, which is definitely not correct.
However, it's perfectly valid to assign a list of cars or a list of planes to an IEnumerable<Vehicle> variable.
IEnumerable<Vehicle> vehicles = cars;
foreach (Vehicle vehicle in vehicles)
{
vehicle.Travel();
}
The quick explanation here is that the IEnumerable interface doesn't allow you to manipulate the collection. It is essentially a read-only interface. The T objects (Vehicles in this case) are only exposed as a return value on the IEnumerable interface's Current property. There are no methods that take Vehicle objects as input parameters, therefore there is no danger of the collection being modified in an illegal way.
Side Note: I've always thought that it would make sense for the IList<T> interface to be a composite of an IReadableList<out T> interface and an IWritableList<in T> interface.
It might help to think about what you could do with your List(Of SomeClass) after you've assigned it to a List(Of ISomeInterface) variable.
You could add any object which implements ISomeInterface to it, e.g. SomeOtherClass, and no longer have a valid List(Of SomeClass)
This is the reason that covariance is not defined for List(Of T) in this case
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With