4

I tried to make a conversion from object (the object contains a List<Apple>) to List<object>, and it fails with an exception:

Unable to cast object of type 'System.Collections.Generic.List[list_cast_object_test.Apple]' to type 'System.Collections.Generic.List[System.Object]'.

When I have replaced List with IEnumerable or IList (an interface) it works well, and I don't understand the difference....

The code looks like:

    private void Form1_Load(object sender, EventArgs e) {
        object resultObject = GetListAsObject();

        // this cast works fine, it's normal...
        var resAsIt = (List<Apple>)resultObject;

        // also when I use a generic interface of 'object' cast works fine
        var resAsIEnumerable = (IEnumerable<object>)resultObject;

        // but when I use a generic class of 'object' it throws me error: InvalidCastException
        var resAsList = (List<object>)resultObject;
    }

    private object GetListAsObject() {
        List<Apple> mere = new List<Apple>();
        mere.Add(new Apple { Denumire = "ionatan", Culoare = "rosu" });
        mere.Add(new Apple { Denumire = "idared", Culoare = "verde" });
        return (object)mere;
    }
}
public class Apple {
    public string Denumire { get; set; }
    public string Culoare { get; set; }
}

Someone may explain me what it happens? What is the difference between cast to a generic interface and cast to a generic class?

Paul Roub
  • 36,322
  • 27
  • 84
  • 93
Ovidiu Rudi
  • 191
  • 1
  • 15
  • 2
    ``IEnumerable`` is covariant – Ehsan Sajjad Feb 03 '16 at 12:46
  • It's the [lion eats giraffe](http://stackoverflow.com/a/2033921/284240) problem. `IEnumerable<>` is safe for giraffes because you can't [add](https://msdn.microsoft.com/en-us/library/3wcytfd1(v=vs.110).aspx) lions. – Tim Schmelter Feb 03 '16 at 12:51

2 Answers2

6

It is because in IEnumerable<T> T is covariant while in List<T> T does not support Covariance.

See more about Covariance and Contraviance in c#

If you want to do thing like that you then have to use ToList<T>() overload which will wrap it to object type like:

 List<object> result = resultObject.ToList<object>();

Here we are able to convert List<Apple> to List<Object> due to new List<T> creation where T is of type Object but we cannot cast the original List<Apple> reference to List<object>.

In this case it will create instance of Object type and will store reference to your type as reference of type object , but in case of Value Types or strcuts it will have boxing cost.

Ehsan Sajjad
  • 61,834
  • 16
  • 105
  • 160
4

IEnumerable<T> is an interface, and since the type T is defined covariant (it is actually IEnumerable<out T>, note the out), you can cast the T to a base implementation, so IEnumerable<object is a valid cast on your list of type List<Apple>.

However, the type List<Apple> is not equal to the type List<object>, nor does the list List<Apple> derived from List<object>. The type parameter on List<T> is not covariant, hence that cast is invalid.

Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325