Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Casting generic container of type to container of inherited type?

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?

like image 633
Michael Avatar asked Jan 18 '23 18:01

Michael


2 Answers

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.

like image 175
Jon Skeet Avatar answered Jan 30 '23 15:01

Jon Skeet


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) { ...
like image 27
JaredPar Avatar answered Jan 30 '23 13:01

JaredPar