2

In C#, I have a class MyObj that implements an interface IMyInterface.

I now have a collection of lists of MyObj class:

IEnumerable<List<MyObj>> myObjGroups

and I want to convert / cast it to

IEnumerable<List<IMyInterface>> myInterfaceGroups

and everything I have tried has thrown an exception.

An exception of type 'System.InvalidCastException' occurred in System.Core.dll but was not handled in user code Additional information: Unable to cast object of type 'System.Collections.Generic.List`1[MyObj]' to type 'System.Collections.Generic.List`1[IMyInterface]'.

I have tried:

IEnumerable<List<IMyInterface>> myInterfaceGroups= new List<List<IMyInterface>>(myObjGroups.Cast<List<IMyInterface>>());

and:

IEnumerable<List<IMyInterface>> myList = myObjGroups.Cast<List<IMyInterface>>();

and both seems to throw exceptions at run time.

Any suggestions on what i am doing wrong?

Jeppe Stig Nielsen
  • 60,409
  • 11
  • 110
  • 181
leora
  • 188,729
  • 360
  • 878
  • 1,366
  • 2
    `List` is not a `List` so the cast is invalid. You could use `IEnumerable>`. If you need inner lists then `myObjGroups.Select(l => l.Cast().ToList())`. – Lee Nov 20 '16 at 13:10
  • 1
    Like @Lee says, a `List` is not a `List`. That is because `List` is not covariant in `T` (it is not `List`). That is because the class has members, such as the `Add` method, that make covariance impossible. An interface such as `IReadOnlyList` would work. An `IReadOnlyList` is an `IReadOnlyList` if just `MyObj` is a reference type implementing `IMyInterface`. And also `IEnumerable` is covariant. – Jeppe Stig Nielsen Nov 20 '16 at 13:40

2 Answers2

2

Try it the following way:

IEnumerable<List<IMyInterface>> myInterfaceGroups = myObjGroups
    .Select(l => l.Select(o => (IMyInterface)o).ToList());

Or if you prefer using the Cast<T>() extension method:

IEnumerable<List<IMyInterface>> myInterfaceGroups = myObjGroups
    .Select(l => l.Cast<IMyInterface>().ToList());

EDIT: A bit of explanation

To better understand why did you get the InvalidCastException exception, let's try to decompose your original expression:

IEnumerable<List<IMyInterface>> myInterfaceGroups = 
    new List<List<IMyInterface>>(myObjGroups.Cast<List<IMyInterface>>());

This is equivalent to:

IEnumerable<List<IMyInterface>> myObjGroupsAsInterfaceList = myObjGroups
    .Cast<List<IMyInterface>>()
    .ToList();

IEnumerable<List<IMyInterface>> myInterfaceGroups = new List<List<IMyInterface>>(myObjGroupsAsInterfaceList);

The Cast<T>() extension method just iterates through the items and tries to cast each item to type T. We could replace the functionality of Cast<T>() extension method combined with ToList<T>() with the following snippet:

List<List<IMyInterface>> myObjGroupsAsInterfaceList = new List<List<IMyInterface>>();
foreach (List<MyObj> myObjGroup in myObjGroups)
{
    List<IMyInterface> myObjGroupAsInterface = myObjGroup; // Compile error!
    myObjGroupsAsInterfaceList.Add(myObjGroupAsInterface);
}

So the root problem is that you cannot assign a List<MyObj> object to a variable of type List<IMyInterface>.

To find more explanation on why the above is not possible, take a look on the following question: C# variance problem: Assigning List<Derived> as List<Base>

Community
  • 1
  • 1
Botond Botos
  • 1,202
  • 13
  • 20
0

You're doing something wrong. You can't cast an IEnumerable to a List. A list is an actual metrialized collection of data, while an run-time IEnumerable needs to be iterated over in order to retrieve the data.

To solve your problem, you need to Cast to IEnumerable<IMyInterface>:

Check a working Fiddle: Here (Same below)

public class Program
{
    static IEnumerable<List<MyObj>> Get()
    {
        yield return new List<MyObj>();
        yield return new List<MyObj>();
    }

    static void Main()
    {
        IEnumerable<List<MyObj>> myObjGroups = Get();

        var result = myObjGroups.Cast<IEnumerable<IMyInterface>>();

        foreach(var val in result)
            Console.WriteLine(val.Count());
    }
}
Zein Makki
  • 29,485
  • 6
  • 52
  • 63