Given the following types:
public interface IPrimary{ void doBattle(); }
// an ISecondary "is" an IPrimary
public interface ISecondary : IPrimary { }
// An implementation of ISecondary is also an IPrimary:
internal class SecondaryImpl : ISecondary
{
// Required, since this is an IPrimary
public void doBattle(){ }
}
Why can I not do this?
List<IPrimary> list = new List<ISecondary>();
This results in the following compilation error:
Argument type 'System.Collections.Generic.List' is not assignable to parameter type 'System.Collections.Generic.List'
I understand the error, and I realize there are workarounds. I just do not see any clear reason why this direct conversion is disallowed. The values contained in a list of ISecondary
, should after all, be (by extension) values of type of IPrimary
.Why then are List<IPrimary>
and List<ISecondary>
being interpreted as unrelated types?
Can anyone explain clearly the reasoning for C# being designed this way?
A slightly extended example: I came across the issue when trying to do something similar to the following:
internal class Program
{
private static void Main(string[] args)
{
// Instance of ISecondary, and by extention, IPrimary:
var mySecondaryInstance = new SecondaryImpl();
// This works as expected:
AcceptImpl(mySecondaryInstance);
// List of instances of ISecondary, which are also,
// by extention, instances of IPrimary:
var myListOfSecondaries = new List<ISecondary> {mySecondaryInstance};
// This, however, does not work (results in a compilation error):
AcceptList(myListOfSecondaries);
}
// Note: IPrimary parameter:
public static void AcceptImpl(IPrimary instance){ }
// Note: List of type IPrimary:
public static void AcceptList(List<IPrimary> list){ }
}
Interfaces can inherit from one or more interfaces. The derived interface inherits the members from its base interfaces. A class that implements a derived interface must implement all members in the derived interface, including all members of the derived interface's base interfaces.
C# allows the user to inherit one interface into another interface. When a class implements the inherited interface then it must provide the implementation of all the members that are defined within the interface inheritance chain.
A class implements an interface, it does not inherit it. However, if you want to provide code for a derived class, you can just write a base class to contain it.
Interfaces don't contain fields because fields represent a specific implementation of data representation, and exposing them would break encapsulation. Thus having an interface with a field would effectively be coding to an implementation instead of an interface, which is a curious paradox for an interface to have!
Why can I not do this?
List<IPrimary> list = new List<ISecondary>();
Imagine that you had a method defined like this:
public void PopulateList(List<IPrimary> listToPopulate)
{
listToPopulate.Add(new Primary()); // Primary does not implement ISecondary!
}
What would happen if you were to pass it a List<ISecondary>
as a parameter?
The error that List<ISecondary>
is not assignable from List<IPrimary>
is the compiler's way of getting you out of such troubles.
public class Animal
{
...
}
public class Cat: Animal
{
public void Meow(){...}
}
List<Cat> cats = new List<Cat>();
cats.Add(new Cat());
cats[0].Meow(); // Fine.
List<Animal> animals = cats; // Pretend this compiles.
animals.Add(new Animal()); // Also adds an Animal to the cats list, since animals references cats.
cats[1].Meow(); // cats[1] is an Animal, so this explodes!
And that's why.
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