Question

Everything inherits from object. It's the basis of inheritance. Everything can be implicitly cast up the inheritance tree, ie.

object me = new Person();

Therefore, following this through to its logical conclusion, a group of People would also be a group of objects:

List<Person> people = new List<Person>();
people.Add(me);
people.Add(you);
List<object> things = people; // Ooops.

Except, that won't work, the people who designed .NET either overlooked this, or there's a reason, and I'm not sure which. At least once I have run into a situation where this would have been useful, but I had to end up using a nasty hack (subclassing List just to implement a cast operator).

The question is this: is there a reason for this behaviour? Is there a simpler solution to get the desired behaviour?

For the record, I believe the situation that I wanted this sort of behaviour was a generic printing function that displayed lists of objects by calling ToString() and formatting the strings nicely.

Was it helpful?

Solution

OK, everyone who has used generics in .net must have run into this at one point or another.

Yes, intuitively it should work. No, in the current version of the C# compiler it doesn't.

Eric Lippert has a really good explanation of this issue (it's in eleven parts or something and will bend you mind in places, but it's well worth the read). See here.

edit:

dug out another relevant link, this one discusses how java handles this. See here

OTHER TIPS

you can use linq to cast it:

IEnumerable<Person> oldList = someIenumarable;
IEnumerable<object> newList = oldlist.Cast<object>()

At first glance, this does not make intuitive sense. But it does. Look at this code:

List<Person> people = new List<Person>();
List<object> things = people; // this is not allowed
// ...
Mouse gerald = new Mouse();
things.add(gerald);

Now we suddenly have a List of Person objects... with a Mouse inside it!

This explains why the assignment of an object of type A<T> to a variable of type A<S> is not allowed, even if S is a supertype of T.

The linq workaround is a good one. Another workaround, since you are using type object, is to pass the list as IEnumerable (not the generic version).

Edit: C# 4 (currently beta) supports a covariant type parameter in IEnumerable. While you won't be able to assign directly to a List<object>, you can pass your list to a method expecting an IEnumerable<object>.

While what your trying to does indeed flow logically, its actually a feature that many languages don't natively support. This is whats called co/contra variance, which has to do with when and how objects can be implicitly cast from one thing to nother by a compiler. Thankfully, C# 4.0 will bring covariance and contravariance to the C# arena, and such implicit casts like this should be possible.

For a detailed explanation of this, the following Channel9 video should be helpful:

http://channel9.msdn.com/shows/Going+Deep/Inside-C-40-dynamic-type-optional-parameters-more-COM-friendly/

With linq extension methods you can do

IEnumerable<object> things = people.Cast<object>();
List<object> things = people.Cast<object>().ToList();

Otherwise since you are strongly typing the list the implicit conversion isn't allowed.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top