views:

106

answers:

3

It is clear that the T[] array type is not covariant as the elements of a T[] can be set by index.

And yet, a U[] can be cast to a T[] without any complaints from the compiler as long as U derives from T.

Man[] men = new[] { new Man("Aaron"), new Man("Billy"), new Man("Charlie") };
Person[] people = (Person[])men;

In the above code it appears that men and people do seem to hold a reference to the same Array object. The effect of setting men[0] = new Man("Aidan") can be seen at people[0]. Similarly attempting people[0] = new Woman("Debbie") results in an ArrayTypeMismatchException at runtime*.

Does this mean that the T[] type actually performs type checking on every set call? It seems that this must be necessary if it is allowed to cast arrays in this manner.

I guess my question is just: How is this possible? It's clear to me that U[] does not derive from T[]. It's also unclear to me whether I could ever define my own type that would work in this way: actually be invariant but act covariant.


*Though array variance is apparently permitted by the CLR, any language could disallow casting between array types. However, it appears that this behavior is identical in VB.NET:

Dim men = New Man() { New Man("Aaron"), New Man("Billy"), New Man("Charlie") }
Dim people = CType(men, Person())
+7  A: 

This is a special behavior of arrays and cannot be replicated in any other type.

It is generally regarded as a mistake.

SLaks
So how does it work with arrays?
Dan Tao
@Dan: Special magic in the CLR. Yes; every array set (of a non-sealed type) performs a typecheck.
SLaks
@Dan Tao did you look at the linked post? It's explained there
jeroenh
@jeroenh: Yeah, I read it once the link was posted (the link was added after my first comment).
Dan Tao
+2  A: 

This works just because it is a special feature that is part of the CLR. Arrays are automatically covariant even though technically they aren't. Compilers just have to know this and generate the proper type checks.

Although some consider this feature a mistake, it was very important to have prior to typesafe collections and generic interface covariance. You would not even be able to write a function to sort an array without it.

Gabe
Compilers do not need to know this, except for casting rules. This is handled by the runtime.
SLaks
+1  A: 

Eric explained it very clearly here: http://blogs.msdn.com/b/ericlippert/archive/2007/10/17/covariance-and-contravariance-in-c-part-two-array-covariance.aspx.

Short resume: if D inherits B then D[] is covariant to B[]. Assume D2 also inherits from B, then

B[] b = new D[10]; 
b[0] = new D1();

throws exception. Yes, this is not good but it is so.

Andrey