4

If I have two classes:

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

and I create a generic container and a function that takes it:

public void Foo(List<A> lst) { ... }

I get an invalid conversion if I attempt casting a List<B> to a List<A>, and instead have to pass it like so:

var derivedList = new List<B>();
Foo(new List<A>(derivedList));

Is there some way to pass a List<B> to this function without the overhead of allocating a brand new list, or does C# not support converting from a generic container of a derived type to its base type?

Michael
  • 701
  • 4
  • 15
  • 1
    see here: http://stackoverflow.com/questions/1817300/convert-list-of-derived-class-objects-to-list-of-base-class-objects – SwDevMan81 Sep 09 '11 at 17:28
  • related: http://stackoverflow.com/questions/686305/converting-a-list-of-base-type-to-a-list-of-inherited-type – SwDevMan81 Sep 09 '11 at 17:33

3 Answers3

7

A List<B> simply isn't a List<A> - after all, you can add a plain A to a List<A>, but not to a List<B>.

If you're using C# 4 and .NET 4 and your Foo method only really needs to iterate over the list, then change the method to:

public void Foo(IEnumerable<A> lst) { ... }

In .NET 4, IEnumerable<T> is covariant in T, which allows a conversion from IEnumerable<B> (including a List<B>) to IEnumerable<A>. This is safe because values only ever flow "out" of IEnumerable<A>.

For a much more detailed look at this, you can watch the video of the session I gave at NDC 2010 as part of the torrent of NDC 2010 videos.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
2

This is not possible. C# doesn't support co / contra variance on concrete types such as List<T>. It does support it on interfaces though so if you switch Foo to the following signature you can avoid an allocation

public void Foo(IEnumerable<A> enumerable) { ...
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
0

If you wish to pass list-like things to routines which are going to read them but not write them, it would be possible to define a generic covariant IReadableList<out T> interface, so that an IReadableList<Cat> could be passed to a routine expecting an IReadableList<Animal>. Unfortunately, common existing IList<T> implementations don't implement any such thing, and so the only way to implement one would be to implement a wrapper class (which could accept an IList as a parameter), but it probably wouldn't be too hard. Such a class should also implement non-generic IList, also as read-only, to allow code to evaluate Count without having to know the type of the items in the list.

Note that an object's implementation of IReadableList<T> should not be regarded as any promise of immutability. It would be perfectly reasonable to have a read-write list or wrapper class implement IReadableList<T>, since a read-write list is readable. It's not possible to use an IReadableList<T> to modify a list without casting it to something else, but there's no guarantee a list passed as IReadableList<T> can't be modified some other way, such as by casting it to something else, or by using a reference stored elsewhere.

supercat
  • 77,689
  • 9
  • 166
  • 211