0

I'm trying to use polymorphism in a constructor but cannot make it work without workaround.

I have :

public class A { }
public class B : A { }

Why doesn't this work :

 IList<B> blist = new List<B> ...
 IList<A> alist = (IList<A>)blist ;

When the same without the list works fine:

B bt = new B..
A a = (A)b;

This is especially annoying when wanting to use a list in a constructor, especially with the c# limitation of calling the base constructor before doing anything else. which forbid to do this :

public X(IList<B> param) : base((IList<A> param))
{}

Any way to do it properly without calling a dummy base() and rewriting the constructor completely ?

One way I found is doing : base( sections.Select(b => (A) b).ToList() )

but It feel quite klunky...

Edit:

This question emphasis on the construction aspect.

For this case I use a List because the order is important and the amount of elements could vary. answers on this post and similar post focus on using Array or Enumerable. However Array is limited when resizing and Enumerable does not guarantee the order.

3 Answers3

1

Answer is simple, A and B share an Is a relationship, whereas List<A> and List<B> do not. Hence this is possible with only A and B:

A a = new B();

However, using the Covariance feature of C# there are a few possibilities which you can explore:

IEnumerable<A> a = new List<B>(); // possible due to covariance.
A[] a = new B[]; // or use an array

It depends on what you want to do with the list in your constructor/class, whether you just want a collection to enumerate or do you want to add/remove elements.

Of course, last option you already know is to cast each element.

YK1
  • 7,327
  • 1
  • 21
  • 28
  • I see, however Enumerable seems not appropriate in my case, since the order of the elements is important. an array could be better though. I might be under-using arrays in c#, probably due to their limitation in c –  Oct 13 '22 at 12:59
  • `IEnumerable` itself does not guarantee order, however, if implementation is `List` order will be retained. Other implementations like `HashSet` does not provide order guarantee. If you use only `List` as implementation, you should be good. – YK1 Oct 13 '22 at 14:04
0

There is a Linq Extension Cast<T>() that simply casts each element to the given type.

IList<B> blist = new List<B>();
IList<A> alist = blist.Cast<A>().ToList();
DevMoth
  • 51
  • 5
0

You'll find that, what does work is,

IEnumerable<B> blist = new List<B>();
IEnumerable<A> alist = (IEnumerable<A>)blist;

Why? This is because IEnumerable<out T> is covariant where as, IList<T> is not.

IList<T> cannot be covariant because of the Add method. So if you want a list instead of a sequence but don't need to mutate the list.

IReadOnlyList<B> blist = new List<B>();
IReadOnlyList<A> alist = (IReadOnlyList<A>)blist;

works too.

Jodrell
  • 34,946
  • 5
  • 87
  • 124