251

While we can inherit from base class/interface, why can't we declare a List<> using same class/interface?

interface A
{ }

class B : A
{ }

class C : B
{ }

class Test
{
    static void Main(string[] args)
    {
        A a = new C(); // OK
        List<A> listOfA = new List<C>(); // compiler Error
    }
}

Is there a way around?

1

15 Answers 15

298

The way to make this work is to iterate over the list and cast the elements. This can be done using ConvertAll:

List<A> listOfA = new List<C>().ConvertAll(x => (A)x);

You could also use Linq:

List<A> listOfA = new List<C>().Cast<A>().ToList();
4
  • 2
    another option: List<A> listOfA = listOfC.ConvertAll(x => (A)x); Commented Jul 16, 2014 at 7:56
  • 7
    Which one is faster? ConvertAll or Cast? Commented Sep 3, 2014 at 10:21
  • 33
    This will create a copy of the list. If you add or remove something in the new list, this won't be reflected in the original list. And secondly, there is a big performance and memory penalty since it creates a new list with the existing objects. See my answer below for a solution without these problems.
    – Bigjim
    Commented Jun 5, 2015 at 9:07
  • Answer to modiX: ConvertAll is a method on List<T> , so it will only work on a generic list; it won't work on IEnumerable or IEnumerable<T>; ConvertAll can also do custom conversions not just casting, e.g. ConvertAll(inches => inches * 25.4). Cast<A> is a LINQ extension method, so works on any IEnumerable<T> (and also works for non-generic IEnumerable), and like of most of LINQ it uses deferred execution, that is, only converts as many items as are retrieved. Read more about it here: codeblog.jonskeet.uk/2011/01/13/…
    – Edward
    Commented Jul 13, 2018 at 14:36
196

First of all, stop using impossible-to-understand class names like A, B, C. Use Animal, Mammal, Giraffe, or Food, Fruit, Orange or something where the relationships are clear.

Your question then is "why can I not assign a list of giraffes to a variable of type list of animal, since I can assign a giraffe to a variable of type animal?"

The answer is: suppose you could. What could then go wrong?

Well, you can add a Tiger to a list of animals. Suppose we allow you to put a list of giraffes in a variable that holds a list of animals. Then you try to add a tiger to that list. What happens? Do you want the list of giraffes to contain a tiger? Do you want a crash? or do you want the compiler to protect you from the crash by making the assignment illegal in the first place?

We choose the latter.

This kind of conversion is called a "covariant" conversion. In C# 4 we will allow you to make covariant conversions on interfaces and delegates when the conversion is known to be always safe. See my blog articles on covariance and contravariance for details. (There will be a fresh one on this topic on both Monday and Thursday of this week.)

8
  • 3
    Would there be anything unsafe about an IList<T> or ICollection<T> implementing non-generic IList, but implementing having the non-generic Ilist/ICollection return True for IsReadOnly, and throw NotSupportedException for any methods or properties that would modify it?
    – supercat
    Commented Jul 28, 2011 at 15:40
  • 55
    Whilst this answer contains quite acceptable reasoning it is not really "true". The simple answer is that C# doesn't support this. The idea of having a list of animals that contains giraffes and tigers is perfectly valid. The only issue comes when you want to access the higher level classes. In reality this is no different to passing a parent class as a parameter to a function and then trying to cast it to a different sibling class. There may be technical issues with implementing the cast but the explanation above does not provide any reason why it would be a bad idea. Commented Jan 29, 2016 at 5:55
  • 3
    @EricLippert Why can we do this conversion using an IEnumerable instead of a List? i.e.: List<Animal> listAnimals = listGiraffes as List<Animal>; is not possible, but IEnumerable<Animal> eAnimals = listGiraffes as IEnumerable<Animal> works.
    – Jéf Bueno
    Commented May 8, 2017 at 14:54
  • 4
    @jbueno: Read the last paragraph of my answer. The conversion there is known to be safe. Why? Because it is impossible to turn a sequence of giraffes into a sequence of animals and then put a tiger into the sequence of animals. IEnumerable<T> and IEnumerator<T> are both marked as safe for covariance, and the compiler has verified that. Commented May 8, 2017 at 15:15
  • 2
    Long, but no real argument. Bunch of apples is a bunch of fruits. Zoo of tigers is the a zoo of animals. And no compiler can make it incorrect. Commented Jul 3, 2021 at 7:08
67

To quote the great explanation of Eric

What happens? Do you want the list of giraffes to contain a tiger? Do you want a crash? or do you want the compiler to protect you from the crash by making the assignment illegal in the first place? We choose the latter.

But what if you want to choose for a runtime crash instead of a compile error? You would normally use Cast<> or ConvertAll<> but then you will have 2 problems: It will create a copy of the list. If you add or remove something in the new list, this won't be reflected in the original list. And secondly, there is a big performance and memory penalty since it creates a new list with the existing objects.

I had the same problem and therefore I created a wrapper class that can cast a generic list without creating an entirely new list.

In the original question you could then use:

class Test
{
    static void Main(string[] args)
    {
        A a = new C(); // OK
        IList<A> listOfA = new List<C>().CastList<C,A>(); // now ok!
    }
}

and here the wrapper class (+ an extention method CastList for easy use)

public class CastedList<TTo, TFrom> : IList<TTo>
{
    public IList<TFrom> BaseList;

    public CastedList(IList<TFrom> baseList)
    {
        BaseList = baseList;
    }

    // IEnumerable
    IEnumerator IEnumerable.GetEnumerator() { return BaseList.GetEnumerator(); }

    // IEnumerable<>
    public IEnumerator<TTo> GetEnumerator() { return new CastedEnumerator<TTo, TFrom>(BaseList.GetEnumerator()); }

    // ICollection
    public int Count { get { return BaseList.Count; } }
    public bool IsReadOnly { get { return BaseList.IsReadOnly; } }
    public void Add(TTo item) { BaseList.Add((TFrom)(object)item); }
    public void Clear() { BaseList.Clear(); }
    public bool Contains(TTo item) { return BaseList.Contains((TFrom)(object)item); }
    public void CopyTo(TTo[] array, int arrayIndex) { BaseList.CopyTo((TFrom[])(object)array, arrayIndex); }
    public bool Remove(TTo item) { return BaseList.Remove((TFrom)(object)item); }

    // IList
    public TTo this[int index]
    {
        get { return (TTo)(object)BaseList[index]; }
        set { BaseList[index] = (TFrom)(object)value; }
    }

    public int IndexOf(TTo item) { return BaseList.IndexOf((TFrom)(object)item); }
    public void Insert(int index, TTo item) { BaseList.Insert(index, (TFrom)(object)item); }
    public void RemoveAt(int index) { BaseList.RemoveAt(index); }
}

public class CastedEnumerator<TTo, TFrom> : IEnumerator<TTo>
{
    public IEnumerator<TFrom> BaseEnumerator;

    public CastedEnumerator(IEnumerator<TFrom> baseEnumerator)
    {
        BaseEnumerator = baseEnumerator;
    }

    // IDisposable
    public void Dispose() { BaseEnumerator.Dispose(); }

    // IEnumerator
    object IEnumerator.Current { get { return BaseEnumerator.Current; } }
    public bool MoveNext() { return BaseEnumerator.MoveNext(); }
    public void Reset() { BaseEnumerator.Reset(); }

    // IEnumerator<>
    public TTo Current { get { return (TTo)(object)BaseEnumerator.Current; } }
}

public static class ListExtensions
{
    public static IList<TTo> CastList<TFrom, TTo>(this IList<TFrom> list)
    {
        return new CastedList<TTo, TFrom>(list);
    }
}
5
  • 1
    I've just used it as MVC view model and got nice universal razor partial view. Amazing idea! I'm so happy that i read this. Commented Oct 22, 2016 at 21:14
  • 6
    I would only add a "where TTo : TFrom" at the class declaration, so the compiler can warn against incorrect usage. Making a CastedList of unrelated types is nonsensical anyway, and making a "CastedList<TBase, TDerived>" would be useless: You can't add regular TBase objects to it, and any TDerived you get from the original List can already be used as a TBase.
    – Wolfzoon
    Commented Nov 2, 2016 at 16:28
  • 2
    @PaulColdrey Alas, a six year head start is to blame.
    – Wolfzoon
    Commented Nov 2, 2016 at 16:31
  • @Wolfzoon: the standard .Cast<T>() hasn't this restriction either. I wanted to make the behaviour the same as .Cast so it is possible to cast a list of Animals to a list of Tigers, and have an exception when it would contain a Giraffe for example. Just as it would be with Cast...
    – Bigjim
    Commented Apr 12, 2017 at 11:13
  • Hi @Bigjim, I'm having problems understanding how to use your wrapper class, to cast an existing array of Giraffes into an array of Animals (base class). Any tips will be appriciated :) Commented Apr 16, 2018 at 11:45
44

If you use IEnumerable instead, it will work (at least in C# 4.0, I have not tried previous versions). This is just a cast, of course, it will still be a list.

Instead of -

List<A> listOfA = new List<C>(); // compiler Error

In the original code of the question, use -

IEnumerable<A> listOfA = new List<C>(); // compiler error - no more! :)

4
  • How to use the IEnumerable in that case exactly?
    – Vladius
    Commented Dec 14, 2015 at 12:12
  • 1
    Instead of List<A> listOfA = new List<C>(); // compiler Error in the original code of the question, enter IEnumerable<A> listOfA = new List<C>(); // compiler error - no more! :)
    – PhistucK
    Commented Dec 14, 2015 at 16:39
  • 1
    The method taking the IEnumerable<BaseClass> as a parameter will allow for the inherited class to be passed in as a List. So there's very little to do but change the parameter type.
    – beauXjames
    Commented Dec 15, 2015 at 17:00
  • 1
    I'm very curious to know why the implicit cast to A works when the objects are in a IEnumerable but not when they are in a List. Does anyone know why? Commented Apr 15, 2023 at 18:05
32

As far as why it doesn't work, it might be helpful to understand covariance and contravariance.

Just to show why this shouldn't work, here is a change to the code you provided:

void DoesThisWork()
{
     List<C> DerivedList = new List<C>();
     List<A> BaseList = DerivedList;
     BaseList.Add(new B());

     C FirstItem = DerivedList.First();
}

Should this work? The First item in the list is of Type "B", but the type of the DerivedList item is C.

Now, assume that we really just want to make a generic function that operates on a list of some type which implements A, but we don't care what type that is:

void ThisWorks<T>(List<T> GenericList) where T:A
{

}

void Test()
{
     ThisWorks(new List<B>());
     ThisWorks(new List<C>());
}
1
  • "Should this work?" - I would yes and no. I see no reason why you shouldn't be able to write the code and have it fail at compile time since it is actually doing an invalid conversion at the time you access FirstItem as type 'C'. There are many analogous ways to blow up C# that are supported. IF you actually want to achieve this functionality for a good reason (and there are many) then bigjim's answer below is awesome. Commented Jan 29, 2016 at 6:19
30

You can only cast to readonly lists. For example:

IEnumerable<A> enumOfA = new List<C>();//This works
IReadOnlyCollection<A> ro_colOfA = new List<C>();//This works
IReadOnlyList<A> ro_listOfA = new List<C>();//This works

And you cannot do it for lists that support saving elements. The reason why is:

List<string> listString=new List<string>();
List<object> listObject=(List<object>)listString;//Assume that this is possible
listObject.Add(new object());

What now? Remember that listObject and listString are the same list actually, so listString now have object element - it shouldn't be possible and it's not.

3
  • 2
    This one was the most understandable answer to me. Specially because it mentions that there is something called IReadOnlyList, which will work because it guarantees that no more element will be added. Commented Oct 14, 2017 at 7:00
  • It's a shame that you can't do similar for IReadOnlyDictionary<TKey, TValue>. Why is that? Commented Nov 30, 2017 at 17:33
  • This is a great suggestion. I use List<> everywhere for convenience, but most of the time I want it to be readonly. Nice suggestion as this will improve my code in 2 ways by allowing this cast and improving where I specify readonly.
    – Rick Love
    Commented Nov 2, 2018 at 14:12
6

For your problem there are several native C# possibilities:

  1. IReadOnlyList, IEnumerable => error free and typesafe. What you probably need.
  2. Use List<> the proper way => error free / not typesafe
  3. Array => typesafe / throws runtime errors
  4. dynamic or List<object> => universal / not typesafe / throws runtime errors

All of them work well! There is no need for any tricky programming!

Interface and class definitions for all of the examples:

using System; using System.Collections.Generic; using System.Linq;

interface IAnimal
{
    public string Name { get; }
}
class Bear : IAnimal
{
    public string BearName = "aBear";
    public string Name => BearName;
}
class Cat : IAnimal
{
    public string CatName = "aCat";
    public string Name => CatName;
}
// Dog has no base class/interface; it isn't related to the other classes
// But it also provides a <Name> property (what a coincidence!)
class Dog
{
    public string DogName = "aDog";
    public string Name => DogName;
}

public class AssignDerivedClass {
    private static string AnyName(dynamic anymal) => anymal switch
    {
        IAnimal animal => animal.Name,
        Dog dog => dog.DogName,
        string s => s,
        _ => "Any unknown Animal"
    };

Here are examples for each of the solutions:

1. IReadOnlyList, IEnumerable: What you probably need

  • Assign your List<C> to an IEnumerable<A> or IReadOnlyList<A>
  • Neither of them can be changed at runtime, i.e. you can't Add or Remove elements.
  • You can still modify your data elements themselves.
  • Using this IEnumerable or IReadOnlyList is very cheap. Just accessing your data through another pointer.
  • But take care: If you Append elements to IEnumerable or IReadOnlyList, then you create a new IEnumerable. Using this IEnumerable improperly may become expensive.

Since no elements can be added, all elements remain of correct type:

public static void TestIEnumerableAndIReadonlyList()
{
    var cats = new List<Cat>()
    {
        new Cat() { CatName = "Cat-3" },
    };
    IEnumerable<IAnimal> animalsEnumerable = cats;
    IReadOnlyList<IAnimal> animalsReadOnlyList = cats;

    var extendedEnumerable = animalsReadOnlyList.Append(new Bear());
    (extendedEnumerable.First() as Cat).CatName = "Cat-3a";

    Console.WriteLine("Cat names: {0}, {1}, {2}, {3}",
                      cats.ElementAt(0).CatName,
                      animalsReadOnlyList[^1].Name,
                      AnyName(animalsEnumerable.Last()),
                      AnyName(extendedEnumerable.ElementAt(1)));
}
// Result => Cat names: Cat-3a, Cat-3a, Cat-3a, aBear

2. Use List<> the proper way: List<A> listOfA = new()

  • Define a List of your interface (not of a derived class)
  • Assign instances of one derived class only - you didn't want to store other classes anyway, did you?

Adding a Cat to a List of Animals which are supposed to be Bears:

public static void TestListOfInterface()
{
    var bears = new List<IAnimal>()
    {
        new Bear() { BearName = "Bear-1" },
    };
    bears.Add(new Cat() { CatName = "Cat-2" });

    string bearNames = string.Join(", ", bears.Select(animal => animal.Name));
    Console.WriteLine($"Bear names: {bearNames}");

    static string VerifyBear(IAnimal bear)
        => (bear as Bear)?.Name ?? "disguised as a bear!!!";

    string bearInfo0 = VerifyBear(bears[0]);
    string bearInfo1 = VerifyBear(bears[1]);
    Console.WriteLine($"One animal is {bearInfo0}, the other one is {bearInfo1}");
}
// Bear names: Bear-1, Cat-2
// One animal is Bear-1, the other one is disguised as a bear!!!

3. Array: Creating a Bear[] array, it is guaranteed that all array elements reference instances of Bear.

  • You can exchange elements, but you can't remove or add new elements.
  • Trying to set a wrong type yields a runtime error.

Bears in an array:

public static void TestArray()
{
    Bear[] bears = { new Bear(), null };
    IAnimal[] bearAnimals = bears;

    try { bearAnimals[1] = new Cat(); } // => ArrayTypeMismatchException
    catch (Exception e) { Console.Error.WriteLine(e); } // type incompatible with array

    bearAnimals[1] = new Bear() { BearName = "Bear-2" };
    Console.WriteLine($"Bear names: {bearAnimals[0].Name}, {bears[1].BearName}");
}
// Result => Bear names: aBear, Bear-2

4. dynamic and List<dynamic>: The most universal solution

  • Type checking at runtime
  • You abandon your compiler error checking support, so handle with care!
  • If you try to add an element of wrong type, you'll only get a runtime error!
  • If you access a non-existing member, you'll only get a runtime error!
  • You can even assign collections of unrelated classes.

Assign your list to dynamic or use List<dynamic>:

public static void TestDynamicListAndArray()
{
    dynamic any = new List<Cat>()   // List of specific class - or of interface
    {
        new Cat() { CatName = "Cat-1" },
        new Cat() { CatName = "Cat-2" },
    };
    try { any[0].BearName = "Bear-1"; } // => RuntimeBinderException
    catch (Exception e) { Console.Error.WriteLine(e); } // Cat has no BearName
    try { any.Add(new Bear()); } // => RuntimeBinderException
    catch (Exception e) { Console.Error.WriteLine(e); } // no matching overload

    any[1].CatName += 'a';
    Console.WriteLine($"Animal names: {any[0].CatName}, {any[1].Name}");

    var mix = new List<dynamic>
    {
        new Bear() {BearName = "Bear-3"},
        new Dog() {DogName = "Dog-4"},
        "Cat-5",  // 100MHz ;-)
    };
    Console.WriteLine($"{AnyName(mix[0])}, {mix[1].Name}, {AnyName(mix[2])}");

    try { Console.WriteLine($"Names: {any[2].Name}"); } // => RuntimeBinderException
    catch (Exception e) { Console.Error.WriteLine(e); } // no definition for 'Name'

    any = new Bear() { BearName = "Bear-6" }; // Scalar - not a List or Array!
    try { Console.WriteLine($"{AnyName(any[0])}"); } // => RuntimeBinderException
    catch (Exception e) { Console.Error.WriteLine(e); } // cannot apply indexing []
}
//Animal names: Bear-1, Cat-2a
//Bear-3, Dog-4, Cat-5
} //end of class AssignDerivedClass
2

You can also use the System.Runtime.CompilerServices.Unsafe NuGet package to create a reference to the same List:

using System.Runtime.CompilerServices;
...
class Tool { }
class Hammer : Tool { }
...
var hammers = new List<Hammer>();
...
var tools = Unsafe.As<List<Tool>>(hammers);

Given the sample above, you can access the existing Hammer instances in the list using the tools variable. Adding Tool instances to the list throws an ArrayTypeMismatchException exception because tools references the same variable as hammers.

1

I personally like to create libs with extensions to the classes

public static List<TTo> Cast<TFrom, TTo>(List<TFrom> fromlist)
  where TFrom : class 
  where TTo : class
{
  return fromlist.ConvertAll(x => x as TTo);
}
0

Because C# doesn't allow that type of inheritance conversion at the moment.

2
  • 6
    First, this is a question of convertibility, not of inheritance. Second, covariance of generic types will not work on class types, only on interface and delegate types. Commented Nov 30, 2009 at 0:40
  • 5
    Well, I can hardly argue with you.
    – Noon Silk
    Commented Nov 30, 2009 at 0:51
0

This is an extension to BigJim's brilliant answer.

In my case I had a NodeBase class with a Children dictionary, and I needed a way to generically do O(1) lookups from the children. I was attempting to return a private dictionary field in the getter of Children, so obviously I wanted to avoid expensive copying/iterating. Therefore I used Bigjim's code to cast the Dictionary<whatever specific type> to a generic Dictionary<NodeBase>:

// Abstract parent class
public abstract class NodeBase
{
    public abstract IDictionary<string, NodeBase> Children { get; }
    ...
}

// Implementing child class
public class RealNode : NodeBase
{
    private Dictionary<string, RealNode> containedNodes;

    public override IDictionary<string, NodeBase> Children
    {
        // Using a modification of Bigjim's code to cast the Dictionary:
        return new IDictionary<string, NodeBase>().CastDictionary<string, RealNode, NodeBase>();
    }
    ...
}

This worked well. However, I eventually ran into unrelated limitations and ended up creating an abstract FindChild() method in the base class that would do the lookups instead. As it turned out this eliminated the need for the casted dictionary in the first place. (I was able to replace it with a simple IEnumerable for my purposes.)

So the question you might ask (especially if performance is an issue prohibiting you from using .Cast<> or .ConvertAll<>) is:

"Do I really need to cast the entire collection, or can I use an abstract method to hold the special knowledge needed to perform the task and thereby avoid directly accessing the collection?"

Sometimes the simplest solution is the best.

0

I've read this whole thread, and I just want to point out what seems like an inconsistency to me.

The compiler prevents you from doing the assignment with Lists:

List<Tiger> myTigersList = new List<Tiger>() { new Tiger(), new Tiger(), new Tiger() };
List<Animal> myAnimalsList = myTigersList;    // Compiler error

But the compiler is perfectly fine with arrays:

Tiger[] myTigersArray = new Tiger[3] { new Tiger(), new Tiger(), new Tiger() };
Animal[] myAnimalsArray = myTigersArray;    // No problem

The argument about whether the assignment is known to be safe falls apart here. The assignment I did with the array is not safe. To prove that, if I follow that up with this:

myAnimalsArray[1] = new Giraffe();

I get a runtime exception "ArrayTypeMismatchException". How does one explain this? If the compiler really wants to prevent me from doing something stupid, it should have prevented me from doing the array assignment.

1
0

May be late.

Conversion to Array can also do the job.

main()
{
   List<Camel> camels = new List<Camel>();
   Reproducton(camels.ToArray());
}


public void Reproducton(Animal[] animals)
{
    foreach(var animal in animals.ToList())
    {
       var baby = animal.Reproduce();
    }
}
0

The fastest way i found is to copy the internal array.

public static List<T2> CastToList<T, T2>(this List<T> list1)
{
    if (list1 is List<T2> t2)
        return t2;

    var len = list1.Count;

    var srcArray = list1.GetItemsInternal(); // get list._items field, cached delegate

    var ret = new List<T2>(len);
    var dstArray = ret.GetItemsInternal(); // get list._items field

    // fast copy, bulk operation
    Array.Copy(srcArray, 0, dstArray, 0, len);
    CollectionsMarshal.SetCount(ret, len);

    return ret;
}

This is in my tests 30x faster than list.Cast<X>().ToList(), especially with lots of elements.
There are probably faster solutions if you use unsafe code etc.

0

Figure I'd throw this here, because if this trips me up, it's usually when I'm passing my IList<Giraffe> to a method that takes a IList<Animal>.

If you can change the method, make it generic with a where constraint. This will enforce type safety such that you can't add any rhinos to your list during the method call, and later you can call the method again with a whole list of rhinos.

public void ProcessAnimals( IList<Animal> animals )

to

public void ProcessAnimals<T>( IList<T> animals ) where T : Animal

Not the answer you're looking for? Browse other questions tagged or ask your own question.