以下代码:
object array = new int[] {-1};
Console.WriteLine("array is uint[]: {0}", array is uint[]);
Console.WriteLine("array[0]: {0:X}", ((uint[])array)[0]);
打印以下内容,没有任何错误:
数组是uint []:是的 array [0]:FFFFFFFF
这似乎是我所特有的,因为它似乎打破了类型安全。执行以下操作也是编译时错误:
int[] array = {-1};
uint[] test = (uint[])array;
这种不一致来自何处?为什么CLR实现这样?
请注意,我不相信这与数组协方差相同。在数组协方差中,允许转换,因为存在隐式引用转换;这不是这里的情况,两种类型都是值类型,并且它们之间只有显式的强制转换
对于数组协方差,运行时还会在某些情况下抛出异常(当赋值没有意义时)。在这种情况下,运行时不会抛出异常,即使将test[0]
分配给Int32
范围之外的值也是如此。
答案 0 :(得分:2)
.NET中的数组以一种相当破碎的方式协变。
C#中的数组在.NET中协变的方式的子集中是协变的。它仍然被打破,但它不允许:
uint[] test = (uint[])new int[10]; // Compiler Error CS0030 C#
// doesn't allow this covariance
但这是一个C#规则; .NET允许它,它允许在数组之间进行分配,其中类型是相同大小的整数(有符号或无符号),或者具有相同大小的底层类型的枚举。
当然,.NET和C#都允许您将任何数组转换为object
,并且从对象到任何数组类型的转换都是合法的,但在运行时可能会失败。因此,uint[] test = (uint[])(object)new int[10];
是允许的,因为它类似于以下步骤:
object temp = new int[10]; // normal enough asignmnt.
uint[] test = (uint[])temp; // C# doesn't allow assigning
// int[] to uint[] but temp is
// object so the compiler
// doesn't know that's what
// you are doing, and .NET
// does allow it.
来自评论:
你知道为什么CLR允许这种转换吗?它是否有助于CLI合规性(即允许没有无符号类型的语言将数组视为有符号类型)
考虑到在CIL中,对于有符号值和无符号值之间的堆栈值,差异较小。如果您使用clt
,它将从堆栈中弹出两个值,并将它们作为有符号值进行比较,无论它们是否已签名,而clt.un
将它们作为无符号值进行比较,无论它们是否为无符号值。同样
在CIL固有的有符号和无符号类型之间可以自由移动。
现在,协方差意味着我们可以分配一个等于或窄于其赋值的值;这就是它包括bivariance(指定更窄的东西)。 C#不考虑int
和uint
bivariant;你必须明确地在它们之间施放,所以将它们包含在协变指派中是没有意义的。