c# - 为什么某些值类型的数组之间的强制转换似乎会破坏类型安全?

标签 c# casting clr

<分区>

以下代码:

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 中的数组以一种相当 splinter 的方式协变。

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.

来自评论:

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 固有的相同大小的有符号和无符号类型之间可以自由移动。

现在,协方差意味着我们可以分配一个等于或小于分配给它的值;那就是它包括双方差(分配更窄的东西)。 C# 不考虑 intuint 双变;您必须在它们之间显式转换,因此将它们包含在协变分配中没有意义。

关于c# - 为什么某些值类型的数组之间的强制转换似乎会破坏类型安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31254834/

相关文章:

c# - 使用反射从复杂类中获取值

c# - 使用静态 Func<> 投影单个记录

c# - 模拟 IConfiguration 的 GetChildren() 以返回 List<string>?

.net - 关于GetHashCode实现的问题

.net-2.0 - 从 .NET 4.0 应用程序链接 .NET 2.0 托管程序集时,使用哪个框架?

windows - 缺少 .NET CLR 性能计数器

c# - 如何在 Web 应用程序中使用 native 库方法?

c# - 如何在 C# 中将泛型类转换为泛型接口(interface)

c++ - 错误 : no operator "!= " matches these operands

c# - 通用类型转换