c# – 为什么在某些值类型的数组之间进行转换似乎打破了类型安全性?

参见英文答案 > 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;你必须在它们之间明确地进行转换,所以将它们包含在协变赋值中是没有意义的.

上一篇:.NET Framework 简介


下一篇:.NET Framework 简介