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

11,744

Solution 1

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.

Solution 2

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

Solution 3

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.

Share:
11,744
Xinbi
Author by

Xinbi

I've been a software engineer for over 20 years in the Portland, Oregon area. I've done online banking, anti-virus software and mobile apps.

Updated on June 14, 2022

Comments

  • Xinbi
    Xinbi almost 2 years

    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?

  • Yinda Yin
    Yinda Yin over 9 years
    Don't sign your posts. They are already signed; see? --------------->
  • Xinbi
    Xinbi over 9 years
    Even an explicit cast doesn't work. IList<ICloneable> cloneables = (IList<ICloneable>)people;
  • Servy
    Servy over 9 years
    The error message does not say that an explicit cast exists, nor is one valid in this context.
  • KC-NH
    KC-NH over 9 years
    Using VS 2013, the error message I see is: Error 1 Cannot implicitly convert type 'System.Collections.Generic.List<Cloneable.Person>' to 'System.Collections.Generic.IList<System.ICloneable>'. An explicit conversion exists (are you missing a cast?) C:\Users\Ken\Documents\Visual Studio 2013\Projects\Samples\Cloneable\Cloneable\Program.cs 29 45 C‌​loneable
  • Matt Burland
    Matt Burland over 9 years
    The explicit cast won't work. It will trade your compile time error for a run-time exception (InvalidCastException).
  • Matt Burland
    Matt Burland over 9 years
    @KC-NH: Where you try to do the cast, obviously.