3

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.

Matt Hamilton
  • 200,371
  • 61
  • 386
  • 320
Matthew Scharley
  • 127,823
  • 52
  • 194
  • 222

6 Answers6

5

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

Community
  • 1
  • 1
Hamish Smith
  • 8,153
  • 1
  • 34
  • 48
4

you can use linq to cast it:

IEnumerable<Person> oldList = someIenumarable;
IEnumerable<object> newList = oldlist.Cast<object>()
mattlant
  • 15,384
  • 4
  • 34
  • 44
  • Note that this is an iterative cast, and will impose a performance hit. Don't confuse the LINQ Cast method with a native C# cast...the two are different. – jrista Aug 19 '09 at 04:04
3

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.

Thomas
  • 174,939
  • 50
  • 355
  • 478
  • Agreed, but wouldn't it be nice to have the same facilities as in the java generics where you can specify 'T ? super MyClass'? – Hamish Smith Sep 21 '08 at 03:28
  • You could always get around this problem by ie, marking the casted list readonly, but your point is well taken. – Matthew Scharley Sep 21 '08 at 04:06
  • Then try the explanation by Eric Lippert, mentioned in the Accepted Answer. It goes into all the details. – Thomas Jun 02 '09 at 13:44
3

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>.

dcstraw
  • 3,243
  • 3
  • 29
  • 38
2

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/

jrista
  • 32,447
  • 15
  • 90
  • 130
1

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.

Quintin Robinson
  • 81,193
  • 14
  • 123
  • 132