6

Is it possible to "upcast" from a generic class based on T, into a generic class based on something more general than T?

For instance, say I have a class named Derived that inherits from a class named Base. Can I ever do something like this:

List<Derived> der = new List<Derived>();
List<Base> bas = (List<Base>) der;

Or, using interfaces, is it ever possible to do something like this:

List<MyClonableType> specific = new List<MyClonableType>();
List<IClonable> general = (List<IClonable>)specific;

As written here, each of these examples fails with an InvalidCastException. The question we're arguing about is whether it's actually impossible, or simply a syntax error that could be fixed if we knew how.

Auraseer
  • 1,245
  • 8
  • 14
  • Java has ` extends X>` for this type of construct, I would be surprised C# doesn't have an equivalent. – Romain Feb 09 '10 at 18:51
  • 1
    C# has generic constraints (the where keyword) for the same thing as extends, but I'm not positive that's what Auraseer is after. He wants to actually use variance on the collection from the looks of it. – Michael Greene Feb 09 '10 at 18:56
  • @Romain, that cast is not valid in C#, but you can define a method like `void Process(List list) where T: Base {...}` – finnw Feb 09 '10 at 18:58
  • @finnw: I see... I guess the `where` constraint covers the typical use cases. The syntax is a bit heavy there, though (but more flexible than Java's) – Romain Feb 09 '10 at 20:33

3 Answers3

3

C# 4.0 will have that feature.

It will be enabled on interfaces and delegates which adhere to certain rules. List<T> won't be supported, as it is a concrete class. IList<T> also won't be supported, as its methods use T in both the in and out positions.

Community
  • 1
  • 1
Bryan Watts
  • 44,911
  • 16
  • 83
  • 88
1

This

List<Derived> der = new List<Derived>(); 
List<Base> bas = (List<Base>)der;

is not possible and should never be possible. What happens here:

Base b = new Base();
bas.Add(b);

Kaboom! is what happens. If the above were legal bas would just refer to der and der can not add instances of Base which is what the Add would be trying to do. (For concreteness, think of Base as Animal and Derived as Cat; you can not cast a List<Cat> to a List<Animal> because then you could add an instance of Dog to the casted list which would fail because it's really a List<Cat>.)

For similar reasons

List<MyClonableType> specific = new List<MyClonableType>();  
List<IClonable> general = (List<IClonable>)specific;  

should never be possible.

In C# 4.0 some of these issues will be solved with the notion of covariance and contravariance. For example, in C# 4.0 this will be legal:

List<Derived> der = new List<Derived>();
IEnumerable<Base> bas = der;

This is because IEnumerable<Base> only spits out instances of Base and since all Deriveds are Bases this is okay. Said another way, IEnumerable<Base> says "I know how to throw instances of Base at you" while List<Derived> says "I know how to throw instances of Derived at you." But as all Deriveds are Bases, this means that List<Derived> also knows how to throw instances of Base at you. Therefore, it should be able to be assigned to an instance of IEnumerable<Base>. This is what is possible in C# 4.0. This is an example of covariance.

I must stress that for types like List<T> which have methods that both eat Ts and spit out Ts it is not possible.

jason
  • 236,483
  • 35
  • 423
  • 525
0

There is nice LINQ extension:

der.ToList<Base>();
Fitzchak Yitzchaki
  • 9,095
  • 12
  • 56
  • 96
  • 1
    I believe this method creates a whole new collection. It can be useful, assuming you've got Linq, but it is not the same as a cast. – Auraseer Feb 09 '10 at 18:58
  • It is true that you can specifically deal with IEnumberable types this way, but it isn't an actual cast as the question asks. In this case, the ToList() method creates a new list of and fills it by casting each item from the original. – Mike Schenk Feb 09 '10 at 19:00
  • If you're only going to *read* the list (which is the only case where the cast would make sense) presumably it does not matter whether you are reading a copy or the original. – finnw Feb 09 '10 at 19:34
  • 1
    finnw, it does matter. If I copy an object and save a reference to the copy, then the original gets changed by some other piece of code, I'll never know about the change. Also, just doing the conversion can be very costly, whereas a cast is pretty close to free. – Auraseer Feb 09 '10 at 19:44