David S.
David S.

Reputation: 11178

object[] as SomeClass[] is always null

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

Answers (3)

Tamas Hegedus
Tamas Hegedus

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

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.

Upvotes: 3

CodeCaster
CodeCaster

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

willeM_ Van Onsem
willeM_ Van Onsem

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

Related Questions