Cannot convert from List<DerivedClass> to List<BaseClass>

C#

C# Problem Overview


I'm trying to pass A list of DerivedClass to a function that takes a list of BaseClass, but I get the error:

cannot convert from 
'System.Collections.Generic.List<ConsoleApplication1.DerivedClass>' 
to 
'System.Collections.Generic.List<ConsoleApplication1.BaseClass>'

Now I could cast my List<DerivedClass> to a List<BaseClass>, but I don't feel comfortable doing that unless I understand why the compiler doesn't allow this.

Explanations that I have found have simply said that it violates type safety somehow, but I'm not seeing it. Can anyone help me out?

What is the risk of the compiler allowing conversion from List<DerivedClass> to List<BaseClass>?


Here's my SSCCE:

class Program
{
    public static void Main()
    {
        BaseClass bc = new DerivedClass(); // works fine
        List<BaseClass> bcl = new List<DerivedClass>(); // this line has an error

        doSomething(new List<DerivedClass>()); // this line has an error
    }

    public void doSomething(List<BaseClass> bc)
    {
        // do something with bc
    }
}

class BaseClass
{
}

class DerivedClass : BaseClass
{
}

C# Solutions


Solution 1 - C#

It is because List<T> is in-variant, not co-variant, so you should change to IEnumerable<T> which supports co-variant, it should work:

IEnumerable<BaseClass> bcl = new List<DerivedClass>();
public void doSomething(IEnumerable<BaseClass> bc)
{
    // do something with bc
}

Information about co-variant in generic

Solution 2 - C#

> Explanations that I have found have simply said that it violates type safety somehow, but I'm not seeing it. What is the risk of the compiler allowing conversion from List<DerivedClass> to List<BaseClass>?

This question is asked almost every day.

A List<Mammal> cannot be converted to a List<Animal> because you can put a lizard into a list of animals. A List<Mammal> cannot be converted to a List<Giraffe> because there might be a tiger in the list already.

Therefore List<T> has to be invariant in T.

However, List<Mammal> can be converted to IEnumerable<Animal> (as of C# 4.0) because there is no method on IEnumerable<Animal> that adds a lizard. IEnumerable<T> is covariant in T.

Solution 3 - C#

The behavior you're describing is called covariance – if A is B, then List<A> is List<B>.

However, for mutable types like List<T>, that is fundamentally unsafe.

Had this been possible, the method would be able to add a new OtherDerivedClass() to a list that can actually only hold DerivedClass.

Covariance is safe on immutable types, although .Net only supports it in interfaces and delegates.
If you change the List<T> parameter to IEnumerable<T>, that will work

Solution 4 - C#

When you have a class derived from a base class, any containers of those classes are not automatically derived. So you cannot just cast a List<Derived> to a List<Base>.

Use .Cast<T>() to create a new list where each object is cast back to the base class:

List<MyDerived> list1 = new List<MyDerived>();
List<MyBase> list2 = list1.Cast<MyBase>().ToList();

Note that this is a new list, not a cast version of your original list, so operations on this new list will not reflect on the original list. Operations on the contained objects will reflect, however.

Solution 5 - C#

If you could write

List<BaseClass> bcl = new List<DerivedClass>(); 

you could call

var instance = new AnotherClassInheritingFromBaseClass();
bc1.Add(instance);

Adding an instance which is not a DerivedClass to the list.

Solution 6 - C#

A solution i used was to create an extension class:

public static class myExtensionClass 
{
    public void doSomething<T>(List<BaseClass> bc) where T: BaseClass
    {
        // do something with bc
    }
}

It uses generic, but when you call it you don't have to especify the class since you already 'told' the compiler the type is the same of extended class.

You would call it this way:

List<DerivedClass> lst = new List<DerivedClass>();
lst.doSomething();

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSam I am says Reinstate MonicaView Question on Stackoverflow
Solution 1 - C#cuongleView Answer on Stackoverflow
Solution 2 - C#Eric LippertView Answer on Stackoverflow
Solution 3 - C#SLaksView Answer on Stackoverflow
Solution 4 - C#Matt HouserView Answer on Stackoverflow
Solution 5 - C#vc 74View Answer on Stackoverflow
Solution 6 - C#MarlonView Answer on Stackoverflow