Reputation: 11178
I came across this strange expression some day, and I think it should work.
var attributes = (new object[]
{
new SomeClass(),
}) as SomeBaseClass[];
In this snippet, SomeClass
inherits SomeBaseClass
, so I think the cast should be valid.
But in fact, attributes
is always evaluated to null
. If I use force cast form, I will get InvalidCastException
.
Upvotes: 3
Views: 152
Reputation: 29926
Normally you shouldn't be able to cast arrays of any type to another, and is a bad practice to do so. For backward compatibility C# has array covariance, which allows a cast in the opposite direction, but not in the direction you want. To properly convert an array, you have to create another array, as shown in this stackoverflow post:
var attributes = (new object[]
{
new SomeClass(),
}).Cast<SomeBaseClass>().ToArray();
Consider the following example:
class A {}
class B : A {}
...
var array_a1 = new A[] { new A(); };
var array_b1 = (B[])array_a; // This is error #1
var array_b2 = new B[] { new B(); };
var array_a2 = (A[])array_b2;
array_a2[0] = new A(); // This is error #2
In the case of error #1, it is clearly seen that array_b1
cannot be a valid view of A[]
, as it could have objects of type A when read. This is a runtime error of InvalidCastException
, happening at the time of the cast.
In the case of error #2, array_a2
is a valid view of array_b2
, but only for reading. The cast itself is allowed due to array covariance, but trying to put an element of type A in there will cause a runtime ArrayTypeMismatchException
, as it would cause array_b2
to have an element of type A. To ensure this kind of type safety the CLR has to do runtime type checks on array assignments, which affects performance.
Upvotes: 3
Reputation: 151604
An object[]
array can store and retrieve anything. It's therefore not possible to just cast it to an array of a more derived type. This is explained in Cast List<X>
to List<Y>
.
Shown there is also the workaround:
SomeType[] typedObjects = someObjectArray.Cast<SomeType>().ToArray();
Note that this will throw an exception if any of the items in someObjectArray
cannot be cast to SomeType
.
Upvotes: 4
Reputation: 476813
This is the correct behavior. Take a look at the specifications of the as
operator:
The as operator is like a cast operation. However, if the conversion isn't possible, as returns null instead of raising an exception. Consider the following example:
expression as type
The code is equivalent to the following expression except that the expression variable is evaluated only one time.
expression is type ? (type)expression : (type)null
Now a conversion from T
to U
does not imply a conversion from T[]
to U[]
: the as
operator is not an intelligent operator in the sense that for an array (or another collection), it will perform an element-wise as
. It only checks if there is a conversion from object[]
to SomeClass[]
, and there is none, so it falls back on the default behavior: return null
.
Consider the following csharp
output from Mono's C# interactive shell:
$ csharp
Mono C# Shell, type "help;" for help
Enter statements below.
csharp> public class Foo {}
csharp> var attributes = new object[] {new Foo(),new Foo(),};
csharp> (Foo[]) attributes
System.InvalidCastException: Cannot cast from source type to destination type.
at <InteractiveExpressionClass>.Host (System.Object& $retval) [0x00000] in <filename unknown>:0
at Mono.CSharp.Evaluator.Evaluate (System.String input, System.Object& result, System.Boolean& result_set) [0x00000] in <filename unknown>:0
at Mono.CSharpShell.Evaluate (System.String input) [0x00000] in <filename unknown>:0
Upvotes: 3