int[]
转换为 object[]
,例如:object[] o = new int[] { 0, 1, 2 };
。但同时,我可以先转换为 object
,再将其转回 int[]
。期待深入的答案。
数组协变特别不适用于值类型的数组。例如,不存在任何转换允许int[]被视为object[]。
int或其他任何值类型的数组都不是对象数组。值类型具有不同的存储特性,与引用类型不同。对象(引用类型)数组保存对象引用列表(对象本身存放在堆中),因此插槽的宽度始终是一个常量。另一方面,值类型直接将其值存储在数组中,因此插槽可能具有任何宽度。这使得两者之间的转换毫无意义。
这有点令人困惑,因为尽管值类型源自System.Object,但它们的行为与引用类型非常不同,而值类型的对象行为(如装箱)仅通过编译器和运行时的神奇处理才可能实现,并且不会扩展到数组。
附带说明,强制转换数组是一个不好的做法,我不会这样做。
若要将类型为A
的实例转换为类型B
,必须满足以下条件之一:
A
到B
有隐式/显式转换;B
派生A
(例如,class A : B {}
)string[]
> object[]
)(*)IEnumerable<string>
> IEnumerable<object>
和Func<string>
> Func<object>
)string Method() {}
可以分配给delegate object Del();
)int[]
强制转换为 object[]
。
(*) - 尽管如此,您应该避免使用数组协变,因为它已经破坏了,并且是为了支持类似Java的语言而添加的(参见此处)。
string[]
和object[]
之间没有层次关系,但是object[] o = new string[] { "0", "1", "2" }
可以工作。正如spender指出的那样,问题在于int
是值类型。 - p.s.w.gint[]
兼容于 object[]
因为 string[]
兼容于 object[]
,那么这将是一个很好的解释。如果是这种情况,那么解释两种情况之间的区别就是相关的。但我认为他困惑的实际原因并不是数组类型是协变的这个事实,我甚至认为他没有意识到这一点。在谈论中引入数组协变性,这个“破碎”的特性和一个特殊情况是不必要的,以我的诚实意见来看。我认为你删除的答案更好。 - dcastroList<int>
强制转换为 List<object>
的原因相同。这样说通了吗? - dcastroint
是一个值类型(如果 OP 使用 string[]
,他/她可能永远不会注意到这一点)。 我没有投反对票,但如果此答案更新以更清楚地说明确切问题,我将投赞成票。 - p.s.w.gstring[]
和 object[]
之间确实存在分层关系 - 这就是协变的定义。同样地,IEnumerable<string>
和 IEnumerable<object>
之间也存在分层关系。 - dcastro尽管类型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[]
不可能是同一个对象。