Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't a list of an interface type accept instances of an inheriting interface? [duplicate]

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){  }

}
like image 939
Kjartan Avatar asked Jan 31 '13 08:01

Kjartan


People also ask

Can interface inherit another interface?

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.

Can interface inherit class in C#?

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.

Can I inherit property from class to interface?

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.

Why interface Cannot contain fields?

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!


2 Answers

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.

like image 53
Cristian Lupascu Avatar answered Oct 05 '22 02:10

Cristian Lupascu


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.

like image 41
Matthew Watson Avatar answered Oct 05 '22 00:10

Matthew Watson