Difference between covariance and upcasting
What is the difference between covarian开发者_如何转开发ce and upcasting, or, more specifically, why are they given different names?
I've seen the following example referred to as 'upcasting':
string s = "hello";
object o = s; //upcast to 'string' to 'object'
Whereas, the following I have seen called 'covariance':
string[] s = new string[100];
object[] o = s;
IEnumerable<string> ies = new List<string>();
IEnumerable<object> ieo = ies;
Now, to my untrained eye, covariance seems to be the same as upcasting, except that it refers the casting of collections. (And of a similar statement can be made regarding contravariance and downcasting).
Is it really that simple?
Now, to my untrained eye, covariance seems to be the same as upcasting, except that it refers the casting of collections. (And of a similar statement can be made regarding contravariance and downcasting).
Is it really that simple?
Covariance isn't about upcasting, although I can see why you think it's related.
Covariance is about the following very simple idea. Let's say you have a variable derivedSequence
of type IEnumerable<Derived>
. Let's say you have a variable baseSequence
of type IEnumerable<Base>
. Here, Derived
derives from Base
. Then, with covariance, the following is a legal assignment, and an implicit reference conversion occurs:
baseSequence = derivedSequence;
Note that this is not upcasting. It is not the case that IEnumerable<Derived>
derives from IEnumerable<Base>
. Rather, it is covariance that allows you to assign the value of the variable derivedSequence
to the variable baseSequence
. The idea is that variables of type Base
can be assigned from objects of type Derived
, and since IEnumerable<T>
is covariant in its parameter, objects of type IEnumerable<Derived>
can be assigned to variables of type IEnumerable<Base>
.
Of course, I haven't yet really explained what covariance is. In general, covariance is about the following simple idea. Let's say you have a mapping F
from types to types (I'll denote this mapping by F<T>
; given a type T
its image under the mapping F
is F<T>
.) Let's say that this mapping has the following very special property:
if
X
is assignment compatible withY
, thenF<X>
is assignment compatible withF<Y>
as well.
In this case, we say that F
is covariant in its parameter T
. (Here, to say that "A
is assignment compatible with B
" where A
and B
are reference types means that instances of B
can be stored in variables of type A
.)
In our case, IEnumerable<T>
in C# 4.0, an implicit reference conversion from instances of IEnumerable<Derived>
to IEnumerable<Base>
if Derived
is derived from Base
. The direction of assignment compatibility is preserved, and this is why we say that IEnumerable<T>
is covariant in its type parameter.
Casting refers to changing the static type of objects and expressions.
Variance refers to the interchangeability or equivalence of types in certain situations (such as parameters, generics, and return types).
IEnumerable<string>
is not derived from IEnumerable<object>
, so the cast between them is not upcasting. IEnumerable is covariant in its type parameter and string is derived from object, so the cast is allowed.
The reason they are different concepts is that, unlike upcasting, covariance is not always allowed. It would have been easy for the designers of the type-system to make IList<Cat>
be considered as "derived" from IList<Animal>
, but then we run into problems:
IList<Cat> cats = new List<Cat>();
IList<Animal> animals = cats;
animals.Add(new Dog()); //Uh oh!
If this were allowed, now our cats
list would contain a Dog
!
In contrast, the IEnumerable<T>
interface has no way of adding elements, so this is perfectly valid (in C# 4.0):
IList<Cat> cats = new List<Cat>();
IEnumerable<Animal> animals = cats;
//There's no way to add things to an IEnumerable<Animal>, so here we are ok
Thie blog post below has a good explanation of it:
http://blogs.msdn.com/b/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx
From what I can gather covariance removes the need for explicit downcasting subsequent to a previous upcast. Typically if you upcast an object you can only access the base type methods and attributes, with covariance it seems you can imply the downcast by replacing lesser derived types with more derived types in the more derived class declaration.
精彩评论