IList and List Conversions with Interfaces

后端 未结 6 1599
攒了一身酷
攒了一身酷 2021-01-03 01:06

I generally understand interfaces, inheritance and polymorphism, but one thing has me puzzled.

In this example, Cat implements IAnimal

相关标签:
6条回答
  • 2021-01-03 01:28

    There is a logical explanation, and this exact question is asked pretty much every day on StackOverflow.

    Suppose this was legal:

    IList<IAnimal> cats = new List<Cat>(); 
    

    What stops this from being legal?

    cats.Add(new Giraffe());
    

    Nothing. "cats" is a list of animals, and a giraffe is an animal, and therefore you can add a giraffe to a list of cats.

    Clearly that is not typesafe.

    In C# 4 we added a feature whereby you can do that if the metadata annotations allow the compiler to prove that it is typesafe. In C# 4 you can do this:

    IEnumerable<IAnimal> cats = new List<Cat>(); 
    

    because IEnumerable<IAnimal> has no Add method, so there is no way to violate type safety.

    See my series of articles on how we designed this feature in C# 4 for more details.

    http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/default.aspx

    (Start from the bottom.)

    0 讨论(0)
  • 2021-01-03 01:36

    C# doesn't support this kind of variance on IList<T> for type-safety reasons.

    If C# did support this, what would you expect to happen here?

    IList<IAnimal> cats = new List<Cat>();
    
    cats.Add(new Dog());         // a dog is an IAnimal too
    cats.Add(new Squirrel());    // and so is a squirrel
    

    In C#4 you're able to do something like this:

    IEnumerable<IAnimal> cats = new List<Cat>();
    

    This is because the IEnumerable<T> interface does support variance of this kind. An IEnumerable<T> is a read-only sequence, so there's no way that you could subsequently add a Dog or a Squirrel to an IEnumerable<IAnimal> that's actually a list of Cat.

    0 讨论(0)
  • 2021-01-03 01:36

    This type of covariance is not supported in C# 4.0. It is reasonable to expect the behavior you want, it just isn't supported (for now).

    0 讨论(0)
  • 2021-01-03 01:37

    You can achieve that using LINQ:

    IList<IAnimal> cats = new List<Cat>().Cast<IAnimal>();
    
    0 讨论(0)
  • 2021-01-03 01:38

    If you need covariance and contravariance on a list-like interface, you should define an interfaces IReadableList<out T> and IWritableList<in T>, and derive a type from List<T> which implements both ReadableList<T&, and WriteableList<T>. This would then make it possible to pass a NewList<Cat> to a routine which expects a ReadableList<Animal> or a WritableList<SiameseCat>.

    0 讨论(0)
  • 2021-01-03 01:48

    IList<T> is not a covariant interface (or it would be IList<out T>). This is because IList both takes type T as a parameter and returns it as a return value from methods, which makes covariance problematic.

    For example, if in your example:

    IList<IAnimal> cats = new List<Cat>();

    You wanted to add a new cat, from cats, it would allow:

    cats.Add(new Dog());

    assuming Dog also implemented IAnimal, this is obviously incorrect and wouldn't work. Which is why IList is not a covariant or contravariant interface.

    0 讨论(0)
提交回复
热议问题