简体   繁体   中英

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 .

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 .

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM