object [] as SomeClass []始终为null

时间:2015-12-25 00:36:28

标签: c# casting type-conversion

有一天我偶然发现了这个奇怪的表情,我觉得它应该有效。

  var attributes = (new object[]
  {
    new SomeClass(),
  }) as SomeBaseClass[];

在此代码段中,SomeClass会继承SomeBaseClass,因此我认为演员表应该有效。

但事实上,attributes始终评估为null。如果我使用强制转换形式,我将获得InvalidCastException

3 个答案:

答案 0 :(得分:4)

object[]数组可以存储和检索任何内容。因此,不可能将其强制转换为更多派生类型的数组。这在Cast List<X> to List<Y>中解释。

显示还有解决方法:

SomeType[] typedObjects = someObjectArray.Cast<SomeType>().ToArray();

请注意,如果someObjectArray中的任何项目无法转换为SomeType,则会引发异常。

答案 1 :(得分:3)

通常你不应该将任何类型的数组转换为另一种类型,这样做是不好的做法。为了向后兼容,C#具有array covariance,它允许在相反方向上进行转换,但不允许在您想要的方向上进行转换。要正确转换数组,您必须创建另一个数组,如this stackoverflow post

所示
var attributes = (new object[]
{
  new SomeClass(),
}).Cast<SomeBaseClass>().ToArray();

为什么能够投射数组呢?

考虑以下示例:

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

错误#1 的情况下,可以清楚地看到array_b1不能是A[]的有效视图,因为它在读取时可能包含A类对象。这是InvalidCastException的运行时错误,发生在演员阵容时。

如果是错误#2 array_a2array_b2的有效视图,但仅供阅读。由于array covariance允许转换本身,但是尝试在其中放置类型A的元素将导致运行时ArrayTypeMismatchException,因为它会导致array_b2具有类型A的元素为了确保这种类型的安全性,CLR必须对阵列分配进行运行时类型检查,这会影响性能。

答案 2 :(得分:3)

这是正确的行为。看一下as运算符的规范:

  

as运算符就像一个强制转换操作。但是,如果无法进行转换,则返回null而不是引发异常。请考虑以下示例:

expression as type
     

代码等同于以下表达式,只是表达式变量只被计算一次。

expression is type ? (type)expression : (type)null

现在从TU的转换意味着从T[]转换为U[]as运算符对于数组(或其他集合)而言,它不是一个智能运算符,它将执行元素明确as。它仅检查是否存在从object[]SomeClass[]的转换,并且没有,因此它会回退到默认行为:return null

考虑Mono的C#交互式shell的以下csharp输出:

$ 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