Suppose I have 2 classes, A
and B
, and B
can be cast to A
. I declared an array of type B[]
called b
. Then if I wanted to cast b
to A[]
, what's the difference between (A[])b
and b.Cast<A>()
?
Your two examples, while different, are both invalid.
You cannot cast an array of one object type to another, even if there exists a conversion operator between them (explicit or implicit). The compiler rightly prevents such a cast. The exception to this rule is if there exists an inheritance relationship; thanks to array covariance you can downcast to a base type (for reference types). The following works:
class A {}
class B : A {}
B[] bs = new[] { new B() };
A[] result = (A[])bs; // valid
See SharpLab
The same principles hold true for the Cast<T>
method in LINQ--unless the types match, an exception will be thrown at runtime upon enumeration. The answer below is incorrect. You cannot, for example, Cast
an array of double
to int
. Of course, if you don't enumerate the result (such as in the example) then no exception occurs. However upon actually enumerating (foreach
, ToList
, ToArray
) an InvalidCastException
will be thrown.
var array = new double[2];
array[0] = 10;
array[1] = 20;
var temp = array.Cast<int>(); // OK, not enumerated
var converted = temp.ToList(); // bam! InvalidCastException
Notice the temp
variable--as in the answer below it doesn't throw thanks to LINQ's deferred execution. It's once you enumerate it that it fails. See SharpLab.
The Cast
method was designed to bridge the gap with pre-generic collections where values were stored internally as an array of object
and the collections themselves only implement IEnumerable
. Cast
allows one to convert to IEnumerable<T>
, however no casting/converting other than from object
to the original type is allowed.
For structs this is obvious--a boxed double
can only be unboxed to a double
; it cannot be unboxed to an int
. Take the simple, non-array case:
double d = 1.5;
object o = d;
int iOk = (int)(double)o; // ok
int iBad = (int)o; // fails
See SharpLab
It makes sense then, that Cast<int>
will fail as the method only inserts the single cast to int
, and not the intermediate cast to double
that would otherwise be required.
For classes, again Cast
will only insert the direct cast. The method is generic and does not/cannot account for any user defined operators. So when you say you "have two classes that can be cast to each other" this still would not matter. In other words, the following will fail:
class A {}
class B {
public static implicit operator A(B b) => new A();
}
B[] bs = new[] { new B() };
var temp = bs.Cast<A>(); // OK, not yet enumerated
A[] result = temp.ToArray(); // throws InvalidCastException
See SharpLab
Again (as above), the exception to this rule is if there exists an inheritance relationship between the two classes. You can downcast from one to the other:
class A {}
class B : A {}
B[] bs = new[] { new B() };
A[] result = bs.Cast<A>().ToArray(); // valid
See SharpLab
One alternative is to use LINQ's Select
to project your original collection, applying the conversion operators you desire:
class A {}
class B {
public static implicit operator A(B b) => new A();
}
B[] bs = new[] { new B() };
A[] result = bs.Select(b => (A)b).ToArray(); // valid!
See SharpLab. This would also work in the case of the double
/int
:
var array = new double[] { 10.2, 20.4 };
int[] result = array.Select(d => (int)d).ToArray();
See SharpLab