3

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.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
David S.
  • 10,578
  • 12
  • 62
  • 104

3 Answers3

4

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.

Community
  • 1
  • 1
CodeCaster
  • 147,647
  • 23
  • 218
  • 272
3

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
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
3

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();

Why is it bad to be able to cast arrays?

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.

Community
  • 1
  • 1
Tamas Hegedus
  • 28,755
  • 12
  • 63
  • 97