Upcasting with a generic type parameter
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>();开发者_C百科
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.
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.
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 Derived
s are Base
s 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 Derived
s are Base
s, 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 T
s and spit out T
s it is not possible.
There is nice LINQ extension:
der.ToList<Base>();
精彩评论