Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot convert source type 'List<Person>' to IList<ISomething>

Tags:

c#

.net

casting

This might be a pretty simple question, but something doesn't make sense to me.

Given this class:

public class Person : ICloneable {
    public object Clone()
    {
        Console.WriteLine("Hello, world");
        return new Person();
    }
}

Why is this ok?

List<Person> people = new List<Person> { new Person() };

IEnumerable<ICloneable> clonables = people;

But this isn't?

List<Person> people = new List<Person> { new Person() };

IList<ICloneable> clonables = people;

Why is it I can assign to an IEnumerable IClonable, but not an IList ICloneable?

like image 449
Xinbi Avatar asked Sep 25 '14 19:09

Xinbi


3 Answers

This is called covariance. Eric Lippert and Jon Skeet (among others) gave some nice explanations of covariance (and its twin, contravariance) in answers to this question: Difference between Covariance & Contra-variance

Very basically, you can enumerate over a list of Person just like you would do over a list of ICloneable, no problem could occur because you can't change the enumeration. But you can't assign your list of Person to a list of ICloneable because then you could later try, for example, to insert some other derivative of ICloneable in it, which would result in a strong violation of type-safety.

like image 82
Patrice Gahide Avatar answered Sep 19 '22 07:09

Patrice Gahide


IList:

public interface IList<T> : ICollection<T>, 
    IEnumerable<T>, IEnumerable

IEnumerable:

public interface IEnumerable<out T> : IEnumerable

Notice the out in IEnumerable? IEnumerable<T> is covariant

like image 23
Matt Burland Avatar answered Sep 22 '22 07:09

Matt Burland


I had a different answer, which was wrong. I apologize. Thanks Matt for pointing this out.

The error message is quite misleading. It suggests a cast will work, but does not. The problem is that the conversion of Person to ICloneable may require adjusting the pointer so that the virtual function table is correct for a generic ICloneable. That means every element in the list may need an adjustment. The real fix is to use ToList:

        IList<ICloneable> clonablesA = people.ToList<ICloneable>();

Ignore some of the comments below, since I completely erased my first answer.

like image 25
KC-NH Avatar answered Sep 23 '22 07:09

KC-NH