246

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?

Bedasso
  • 1,652
  • 1
  • 14
  • 17
Asad
  • 21,468
  • 17
  • 69
  • 94
  • 1
    http://stackoverflow.com/questions/16966961/cannot-convert-from-listderivedclass-to-listbaseclass – cuongle Feb 25 '16 at 20:49

13 Answers13

293

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();
Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
195

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.)

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 3
    Would there be anything unsafe about an IList or ICollection 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 Jul 28 '11 at 15:40
  • 54
    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. – Paul Coldrey Jan 29 '16 at 05:55
  • 3
    @EricLippert Why can we do this conversion using an `IEnumerable` instead of a `List`? i.e.: `List listAnimals = listGiraffes as List;` is not possible, but `IEnumerable eAnimals = listGiraffes as IEnumerable` works. – Jéf Bueno May 08 '17 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` and `IEnumerator` are both marked as safe for covariance, and the compiler has verified that. – Eric Lippert May 08 '17 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. – IgorZhurbenko Jul 03 '21 at 07:08
  • 1
    @EricLippert What about `ImmutableArray` since they are immutable this woudn't be an issue, right? – DomenPigeon Aug 16 '23 at 07:30
  • 2
    @DomenPigeon: Great question. Yes, an immutable array of apples can safely be used as an immutable array of fruit. That is precisely why IEnumerable can be made covariant; there is no "mutate this member" function in the interface. Unfortunately we have no way of expressing in a type "the implementation is immutable". – Eric Lippert Aug 17 '23 at 22:17
  • 1
    @DomenPigeon: In some blog article back in the day I showed how you could create an immutable stack, and if you pushed a giraffe onto a stack of tigers, you could get a stack of mammals safely. There are lots of fun things you can do like that if you can make the type system cooperate. – Eric Lippert Aug 17 '23 at 22:19
66

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);
    }
}
Bigjim
  • 2,145
  • 19
  • 19
  • 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. – Stefan Cebulak Oct 22 '16 at 21:14
  • 5
    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" 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 Nov 02 '16 at 16:28
  • 2
    @PaulColdrey Alas, a six year head start is to blame. – Wolfzoon Nov 02 '16 at 16:31
  • @Wolfzoon: the standard .Cast() 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 Apr 12 '17 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 :) – Denis Vitez Apr 16 '18 at 11:45
41

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! :)

PhistucK
  • 2,466
  • 1
  • 26
  • 28
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>());
}
Chris Pitman
  • 12,990
  • 3
  • 41
  • 56
  • "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. – Paul Coldrey Jan 29 '16 at 06:19
29

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.

  • 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. – bendtherules Oct 14 '17 at 07:00
  • It's a shame that you can't do similar for IReadOnlyDictionary. Why is that? – Alastair Maw Nov 30 '17 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 Nov 02 '18 at 14:12
4

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
uwose
  • 49
  • 4
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);
}
vikingfabian
  • 451
  • 5
  • 5
0

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

Noon Silk
  • 54,084
  • 6
  • 88
  • 105
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.

Community
  • 1
  • 1
Zach
  • 63
  • 5
0

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.

Drew
  • 169
  • 1
  • 1
  • 11
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.

Rajeev Goel
  • 1,395
  • 8
  • 8
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();
    }
}
Prem
  • 303
  • 2
  • 9