c#

list

inheritance

collections

covariance

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?

Solution 1

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();

Solution 2

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

Solution 3

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);
    }
}

Solution 4

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

Solution 5

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>());
}

Solution 6

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.

Solution 7

For your problem there are several native C# possibilities:

  1. dynamic

  2. Array

  3. IReadOnlyList, IEnumerable

  4. Use List<> the proper way.

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

Here are examples for each of them:

1. 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!
  • you even can assign collections of unrelated classes.

Simply write dynamic listOfA = new List<C>(); instead of List<A> listOfA = new List<C>();

At first the 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
class Dog
{
    public string DogName = "aDog";
    public string Name => DogName;
}

Here is the example using dynamic

public class AssignDerivedClass
{
    public static void TestDynamicListAndArray()
    {
        dynamic any = new List<Bear>()   // List of derived
        {
            new Bear() { BearName = "Bear-1" },
            new Bear() { BearName = "Bear-2" }
        };
        //any[0].CatName = "NewCat"; // => Microsoft.CSharp.RuntimeBinder.RuntimeBinderException
        Console.WriteLine($"Bear names: {any[0].BearName}, {Name(any[1])}");

        any = new Cat[]   // Array of derived
        {
            new Cat() { CatName = "Cat-3" },
            new Cat() { CatName = "Cat-4" }
        };
        Console.WriteLine($"Cat names: {any[0].CatName}, {any[1].Name}");

        any = new List<Dog>()   // List of non-related class
        {
            new Dog() { DogName = "Dog-5" },
            new Dog() { DogName = "Dog-6" }
        };
        Console.WriteLine($"Dog names: {any[0].DogName}, {Name(any[1])}");

        any = new List<IAnimal>()   // List of interface
        // any = new IAnimal[]   // Array of interface works the same
        {
            new Bear() { BearName = "Bear-7" },
            new Cat() { CatName = "Cat-8" }
        };
        Console.WriteLine($"Animal names: {any[0].BearName}, {any[1].CatName}");

        any[0].BearName = "NewBear";
        Console.WriteLine($"Animal names: {Name(any[0])}, {any[1].Name}");
    }

    private static string Name(dynamic anymal)
    {
        return anymal switch
        {
            Bear bear => bear.BearName,
            Cat cat => cat.CatName,
            Dog dog => dog.DogName,
            _ => "No known Animal"
        };
    }
    // Bear names: Bear-1, Bear-2
    // Cat names: Cat-3, Cat-4
    // Dog names: Dog-5, Dog-6
    // Animal names: Bear-7, Cat-8
    // Animal names: NewBear, Cat-8
}

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

      public static void TestArray()
      {
          Bear[] bears = { new Bear(), null };
          IAnimal[] bearAnimals = bears;
    
          //bearAnimals[1] = new Cat(); // System.ArrayTypeMismatchException
          bearAnimals[1] = new Bear() { BearName = "Bear-1" };
          Console.WriteLine($"Bear names: {bearAnimals[0].Name}, {bears[1].BearName}");
      }
      // Result => Bear names: aBear, Bear-1
    

3. IReadOnlyList, IEnumerable:

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

  • Why should the compiler allow assigning your List<C> to a List<A> instead of IReadOnlyList<A> when adding an element will lead to an error anyway?

      public static void TestIEnumerableAndIReadonlyList()
      {
          var cats = new List<Cat>()
          {
              new Cat() { CatName = "Cat-3" },
              new Cat() { CatName = "Cat-4" }
          };
          IEnumerable<IAnimal> iEnumerable = cats;
          Console.WriteLine($"Cat names: {(iEnumerable.ElementAt(0) as Cat).CatName}, "
              + Name(iEnumerable.Last()));
    
          IReadOnlyList<IAnimal> iROList = cats;
          Console.WriteLine($"Cat names: {iROList[0].Name}, {Name(iROList[1])}");
    
          //iROList.Add(new Cat()); // compiler error CS61: no definition for 'Add'
      }
      // Result:
      // Cat names: Cat-3, Cat-4
      // Cat names: Cat-3, Cat-4
    

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

  • Define a List of your interface

  • Assign instances of one derived class only - you didn't want to store other classes anyway, did you?

      public static void TestListOfInterface()
      {
          var bears = new List<IAnimal>()
          {
              new Bear() { BearName = "Bear-1" },
              new Cat() { CatName = "Cat-3" },
          };
          bears.Add(new Bear() { BearName = "Bear-2" });
    
          string bearNames = string.Join(", ", bears.Select(animal => animal.Name));
          Console.WriteLine($"Bear names: {bearNames}");
    
          string bearInfo0 = VerifyBear(bears[0]);
          string bearInfo1 = VerifyBear(bears[1]);
          Console.WriteLine($"One animal is {bearInfo0}, the other one is {bearInfo1}");
    
          string VerifyBear(IAnimal bear)
              => (bear as Bear)?.BearName ?? "disguised as a bear!!!";
      }
      // Bear names: Bear-1, Cat-3, Bear-2
      // One animal is Bear-1, the other one is disguised as a bear!!!
    

Solution 8

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);
}

Solution 9

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

Solution 10

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.

Solution 11

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.

Solution 12

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.