参见英文答案 > Why does my C# array lose type sign information when cast to object? 4个
以下代码:
object array = new int[] {-1};
Console.WriteLine("array is uint[]: {0}", array is uint[]);
Console.WriteLine("array[0]: {0:X}", ((uint[])array)[0]);
打印以下内容,没有任何错误:
array is uint[]: True
array[0]: FFFFFFFF
这似乎是我所特有的,因为它似乎打破了类型安全.执行以下操作也是编译时错误:
int[] array = {-1};
uint[] test = (uint[])array;
这种不一致来自何处?为什么CLR实现这样?
请注意,我不相信这与数组协方差相同.在数组协方差中,允许转换,因为存在隐式引用转换;这不是这里的情况,其中两种类型都是值类型,并且它们之间只有显式的强制转换.
对于数组协方差,运行时还会在某些情况下抛出异常(当赋值没有意义时).在这种情况下,运行时不会抛出异常,即使将test [0]分配给Int32范围之外的值也是如此.
解决方法:
.NET中的数组以一种相当破碎的方式协变.
C#中的数组在.NET中协变的方式的子集中是协变的.它仍然破碎,但它不允许:
uint[] test = (uint[])new int[10]; // Compiler Error CS0030 C#
// doesn't allow this covariance
但这是一个C#规则; .NET允许它,它允许在数组之间进行分配,其中类型是相同大小的整数(有符号或无符号),或者具有相同大小的底层类型的枚举.
当然,.NET和C#都允许您将任何数组转换为对象,并且从对象转换为任何数组类型都是合法的,但在运行时可能会失败.所以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.
来自评论:
Do you know why the CLR allows this conversion at all? Does it help with CLI compliance maybe (ie. allow languages without unsigned types to treat arrays as signed types)
好吧,在CIL中,对于有符号值和无符号值之间的堆栈值,差异较小.如果使用clt,它将从堆栈中弹出两个值,并将它们作为有符号值进行比较,无论它们是否有符号,而clt.un将它们作为无符号值进行比较,无论它们是否为无符号值.同样
在CIL固有的相同大小的有符号和无符号类型之间可以*移动.
现在,协方差意味着我们可以分配一个等于或窄于其赋值的值;这就是它包括bivariance(指定更窄的东西). C#不考虑int和uint bivariant;你必须在它们之间明确地进行转换,所以将它们包含在协变赋值中是没有意义的.