将int[]转换为object[]

6
我遇到了这样一个问题:为什么无法将 int[] 转换为 object[],例如:object[] o = new int[] { 0, 1, 2 };。但同时,我可以先转换为 object,再将其转回 int[]
期待深入的答案。

请阅读:http://msdn.microsoft.com/zh-cn/library/aa664572(v=vs.71).aspx - spender
1
旁注,与此无关:不要“强制转换”数据。使用适当的数据类型来处理相应的数据。 - Federico Berasategui
3个回答

10
直接从文档中得知:

数组协变特别不适用于值类型的数组。例如,不存在任何转换允许int[]被视为object[]。

int或其他任何值类型的数组都不是对象数组。值类型具有不同的存储特性,与引用类型不同。对象(引用类型)数组保存对象引用列表(对象本身存放在堆中),因此插槽的宽度始终是一个常量。另一方面,值类型直接将其值存储在数组中,因此插槽可能具有任何宽度。这使得两者之间的转换毫无意义。

这有点令人困惑,因为尽管值类型源自System.Object,但它们的行为与引用类型非常不同,而值类型的对象行为(如装箱)仅通过编译器和运行时的神奇处理才可能实现,并且不会扩展到数组。

附带说明,强制转换数组是一个不好的做法,我不会这样做。


这是一个意识形态问题。我从未使用过这样的代码。有个人问了我,我无法回答。 我们可以将值从堆栈装箱到堆中,然后取出引用并保存到object[]中吗?我认为是可以的。那么为什么你说像这样进行强制转换是没有意义的呢? - Maxim Gorokhovich
因为值类型和引用类型完全不同,而从 System.Object 派生值类型本质上是一种欺骗,当应用于数组时就会崩溃。 - spender

1

若要将类型为A的实例转换为类型B,必须满足以下条件之一:

  1. AB隐式/显式转换;
  2. 存在一种层次关系。这种关系可以通过以下两种方式之一实现:
    1. B派生A(例如,class A : B {}
    2. 协变/逆变。C#允许在以下情况下进行协变:
      1. 引用类型的数组(string[] > object[](*)
      2. 接口/委托中的泛型类型参数(IEnumerable<string> > IEnumerable<object>Func<string> > Func<object>
      3. 委托(string Method() {}可以分配给delegate object Del();
因为上述条件均不成立,所以您不能将 int[] 强制转换为 object[]

(*) - 尽管如此,您应该避免使用数组协变,因为它已经破坏了,并且是为了支持类似Java的语言而添加的(参见此处)


string[]object[]之间没有层次关系,但是object[] o = new string[] { "0", "1", "2" }可以工作。正如spender指出的那样,问题在于int是值类型。 - p.s.w.g
@p.s.w.g 如果 OP 期望 int[] 兼容于 object[] 因为 string[] 兼容于 object[],那么这将是一个很好的解释。如果是这种情况,那么解释两种情况之间的区别就是相关的。但我认为他困惑的实际原因并不是数组类型是协变的这个事实,我甚至认为他没有意识到这一点。在谈论中引入数组协变性,这个“破碎”的特性和一个特殊情况是不必要的,以我的诚实意见来看。我认为你删除的答案更好。 - dcastro
换句话说,我认为促使他提出这个问题的原因,与促使他问为什么无法将 List<int> 强制转换为 List<object> 的原因相同。这样说通了吗? - dcastro
我同意这很可能是混淆的根源。然而,关于层次关系的声明也是无关紧要的,因为某些数组可以被转换,尽管不存在层次关系,它可能会导致更多的混淆。 OP代码不起作用的关键原因是 int 是一个值类型(如果 OP 使用 string[],他/她可能永远不会注意到这一点)。 我没有投反对票,但如果此答案更新以更清楚地说明确切问题,我将投赞成票。 - p.s.w.g
@p.s.w.g 我添加了一条注释,只是为了让它更完整。但现在我在想... "分层关系" 真的无关紧要吗?string[]object[] 之间确实存在分层关系 - 这就是协变的定义。同样地,IEnumerable<string>IEnumerable<object> 之间也存在分层关系。 - dcastro
显示剩余2条评论

0

尽管类型System.Int32派生自对象,并且对System.Int32对象实例的引用可以用作对System.Object的引用,但是System.Int32[]类型的数组不包含System.Int32的实例,也不包含对它们的引用。相反,数组的每个元素将仅保存与Int32相关联的32位数值,而不保存与对象实例相关的任何其他信息。尽管C#允许这样的代码:

Object[] array = new Object[3];
int five = 5;
array[0] = five;
array[1] = five;
array[2] = array[0];

代码并不将five或其引用存储到数组中。相反,对array[0]的赋值将创建一个新的System.Int32类型对象,它保存数字5并存储对该对象的引用。对array[1]的赋值将创建另一个新的System.Int32类型对象,它也保存值5并存储对该对象的引用。第三个赋值将在array[2]中存储对array[0]相同的对象的引用。请注意,即使所有三个数组槽似乎都保存数字5,它们实际上包含更多信息。数组还封装了这样一个事实,即array[0]array[2]保存对同一对象的引用,而array[1]保存对另一个对象的引用。

当引用类型转换为其父类型时,所得到的引用需要标识与原始对象相同的对象。因此,由所得到的引用标识的对象不能封装比原始对象更多的信息(毕竟它们是同一个对象!)。因为一个 Object[],即使其所有元素都标识为 System.Int32 的实例,也封装了超出可以存储在 int[] 中的信息,所以一个 int[] 和一个 Object[] 不可能是同一个对象。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接