C# List<Interface>: why you cannot do `List<IFoo> foo = new List<Bar>();`

C#Generics

C# Problem Overview


If you have an Interface IFoo and a class Bar : IFoo, why can you do the following:

List<IFoo> foo = new List<IFoo>();  
foo.Add(new Bar());

But you cannot do:

List<IFoo> foo = new List<Bar>();

C# Solutions


Solution 1 - C#

At a casual glance, it appears that this should (as in beer should be free) work. However, a quick sanity check shows us why it can't. Bear in mind that the following code will not compile. It's intended to show why it isn't allowed to, even though it looks alright up until a point.

public interface IFoo { }
public class Bar : IFoo { }
public class Zed : IFoo { }

//.....

List<IFoo> myList = new List<Bar>(); // makes sense so far

myList.Add(new Bar()); // OK, since Bar implements IFoo
myList.Add(new Zed()); // aaah! Now we see why.

//.....

myList is a List<IFoo>, meaning it can take any instance of IFoo. However, this conflicts with the fact that it was instantiated as List<Bar>. Since having a List<IFoo> means that I could add a new instance of Zed, we can't allow that since the underlying list is actually List<Bar>, which can't accommodate a Zed.

Solution 2 - C#

The reason is that C# does not support co- and contravariance for generics in C# 3.0 or earlier releases. This is being implemented in C# 4.0, so you'll be able to do the following:

IEnumerable<IFoo> foo = new List<Bar>();

Note that in C# 4.0, you can cast to IEnumerable<IFoo>, but you won't be be able cast to List<IFoo>. The reason is due to type safety, if you were able to cast a List<Bar> to List<IFoo> you would be able to add other IFoo implementors to the list, breaking type safety.

For more background on covariance and contravariance in C#, Eric Lippert has a nice blog series.

Solution 3 - C#

If you need to convert a list to a list of a base class or interface you can do this:

using System.Linq;

---

List<Bar> bar = new List<Bar>();
bar.add(new Bar());

List<IFoo> foo = bar.OfType<IFoo>().ToList<IFoo>();

Solution 4 - C#

It is to do with the creation of the List, you have specified the T to be IFoo therefore you cannot instantiate it as a Bar since they are different types, even though Bar supports IFoo.

Solution 5 - C#

I used some linq to make the conversion easier

List<Bar> bar = new List<Bar>();
bar.add(new Bar());

List<IFoo> foo = bar.Select(x => (IFoo)x).ToList();

It is a little less versbose than other answers but works well

Solution 6 - C#

If you have a list of type List<IFoo> you can call list.add(new Baz()); assuming Baz implements IFoo. However you can't do that with a List<Bar>, so you can't use a List<Bar> everywhere you can use a List<IFoo>.

However since Bar implements IFoo, you can use a Bar everywhere you use IFoo, so passing a Bar to add works when it expects and IFoo.

Solution 7 - C#

Because a list of IFoos can contain some Bars as well, but a list of IFoos is not the same thing as a list of Bars.

Note that I used English above instead of using C#. I want to highlight that this is not a deep problem; you are just getting confused by the details of the syntax. To understand the answer you need to see beyond the syntax and think about what it actually means.

A list of IFoos can contain a Bar, because a Bar is an IFoo as well. Here we're talking about the elements of the list. The list is still a list of IFoos. We haven't changed that.

Now, the list you called foo is still a list of IFoos (more pedantically, foo is declared as a List<IFoo>). It cannot be anything else. In particular, it cannot be made into a list of Bars (List<Bar>). A list of Bar is a completely different object than a list of IFoos.

Solution 8 - C#

List is the type in this case and it's not an inheritance question List<IFoo> really is different than List<Bar>. List doesn't know anythign of, or inherit the characteristics of either IFoo or Bar.

Hope that helps.

Solution 9 - C#

List<Bar> does not inherit from List<IFoo>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionnewBView Question on Stackoverflow
Solution 1 - C#Adam RobinsonView Answer on Stackoverflow
Solution 2 - C#Dustin CampbellView Answer on Stackoverflow
Solution 3 - C#dradersView Answer on Stackoverflow
Solution 4 - C#NathanView Answer on Stackoverflow
Solution 5 - C#PatAttackView Answer on Stackoverflow
Solution 6 - C#sepp2kView Answer on Stackoverflow
Solution 7 - C#Euro MicelliView Answer on Stackoverflow
Solution 8 - C#Dan BlairView Answer on Stackoverflow
Solution 9 - C#Amy BView Answer on Stackoverflow