Sebi
Sebi

Reputation: 3979

Generics List<List<T>>

If I have List<T> which got some lists from type MyClass, for example List<List<MyClass>> and MyClass is the parent class from MyClassB. Why I can't do the following?

List<List<MyClass>> allLists = new List<List<MyClass>>();

List<MyClassB> myList = new List<MyClassB>();
myList.Add(new MyClassB());

//And now the point which dont work
allLists.Add(myList);

If I implement a method I can say SomeClass<T> ... where T : MyClass, is there something similar for my list problem?

So that I can add lists from any child class to my first-level list?

Upvotes: 1

Views: 258

Answers (8)

Tormod
Tormod

Reputation: 4583

Even though MyClassB is a sub type of MyClass, it doesn't mean that List is a sub type of List. The question you want to ask is why List isn't covariant with T (which would cause List to be a subtype of List where T is a subtype of Y. Look up the terms covariance and contravariance.

The answer is two fold. Firstly List was implemented before C# implemented co- and contravariance. But most importantly, you can only be covariant if T is an "out" type. Since you can shove stuff into a list, it is not an out type. IEnumerable only emits objects of type T. Therefore, it can be covariant.

Upvotes: 0

Eric Lippert
Eric Lippert

Reputation: 660463

class Animal {}
class Tiger : Animal {}
class Giraffe : Animal {}
...
List<Giraffe> giraffes = new List<Giraffe>();
List<List<Animal>> lists = new List<List<Animal>>();
lists.Add(giraffes); // Illegal!

Your question is "why is that illegal?" The answer is: suppose it were legal and let's keep going...

List<Animal> animals = lists[0]; // Obviously typesafe.
animals.Add(new Tiger()); // Obviously typesafe

And we just added a tiger to a list of giraffes.

Since the two latter steps are obviously typesafe, the place that cannot be typesafe must be Add(giraffes).

Now, as of C# 4, this does work:

List<IEnumerable<Animal>> lists = new List<IEnumerable<Animal>>();
lists.Add(giraffes);

Why is that legal? Because there is no Add method on IEnumerable<T>:

IEnumerable<Animal> animals = lists[0];

And now we can't violate type safety because there's no way to put a tiger into that list of giraffes if we only are accessing it via IEnumerable<T>.

By the way, someone asks this question almost every day. Do a web search for "C# covariance and contravariance" and you'll get a lot more information about it.

Upvotes: 9

Philipp Sch
Philipp Sch

Reputation: 348

You need to tell the compiler that MyClass is the parent from MyClassB i think. This article about the where keyword may help you further

Upvotes: 0

Alex Tsvetkov
Alex Tsvetkov

Reputation: 1659

This will fix the issue:

List<MyClass> myList = new List<MyClass>();

Upvotes: 0

thesheps
thesheps

Reputation: 655

The problem is that AllLists is strongly-typed to contain lists of MyClass. MyList is strongly-typed to contain instances of MyClassB.

Whereas you'd be able to store List in your List>, there is a type mismatch in your current code.

Upvotes: 0

Knaģis
Knaģis

Reputation: 21495

You have to use as the inner collection an interface that allows derived classes (called covariant interface):

var allLists = new List<IEnumerable<MyClass>>();

Upvotes: 2

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726987

The reason that you cannot do it is as follows: imagine that allLists.Add(myList) worked. Then the compiler would know that allLists[0] is a List<MyClass>, so the following would be OK:

allLists[0].Add(new MyClassX());

That would be a runtime error, because allLists[0] is actually a List<MyClassB>. It is not capable of holding MyClassX objects.

If you change your code so that myList is a List<MyClass>, your code would work:

List<MyClass> myList = new List<MyClass>();
myList.Add(new MyClassB()); // This works, because MyClassB extends MyClass
allLists.Add(myList); // This works, too

Upvotes: 8

Dilshod
Dilshod

Reputation: 3331

List<List<MyClass>> allLists = new List<List<MyClass>>();

the list you are adding is the not same type. You may do like this:

interface IMyClass
{
    //some properties
}

All your child classes must inherit from IMyClass. Then you will have list like this.

List<List<IMyClass>> allLists = new List<List<IMyClass>>();

Upvotes: 0

Related Questions