数组创建表达式和长尺寸长度

我只是阅读C#规范和数组创建表达式的部分。 在规范中它说:

array-creation-expression: new non-array-type [ expression-list ] rank-specifiersopt array-initializeropt new array-type array-initializer new rank-specifier array-initializer 

[剪断]

表达式列表的维度长度表达式按从左到右的顺序进行计算。 在评估每个表达式之后,执行对以下类型之一的隐式转换(第6.1节):int,uint,long,ulong。 选择此列表中存在隐式转换的第一种类型。 如果对表达式的计算或后续的隐式转换导致exception,则不会评估其他表达式,也不会执行其他步骤。

兴奋,我想嗯我不认为我已经看到了,让我们尝试一个很长的尺寸:

 bool[] bb = new bool[2L + Int32.MaxValue]; bb[int.MaxValue + 1L] = true; 

Visual Studio在指向第一行时说:

未处理的exception:System.OverflowException:算术运算导致溢出。

请注意,这不是“OutOfMemoryException”。 如果我更改我的数组创建表达式并使其更小:

 bool[] bb = new bool[Int32.MaxValue]; 

这次我得到一个“OutOfMemoryException”。 我知道CLR的整个“没有对象可以大于2GB”的限制。 我的问题是,当长度不再可以转换为Int32时,为什么我会得到一个非常不同的exception(OverflowException vs OutOfMemoryException)?

编译器可以根据维度计算的输入推断出更大的整数类型,但这并不意味着数组的长度可以超出限制。 编译器基本上将值转换为已检查上下文中的本机整数,使用将抛出溢出的操作码。 这是为了防止值包装或以其他方式允许负数作为维度。

作为示例,请注意此处的数组声明:

 var array = new int[2L + int.MaxValue]; 

由此产生的IL

 IL_0001: ldc.i4 01 00 00 80 IL_0006: conv.u8 IL_0007: conv.ovf.i IL_0008: newarr System.Int32 IL_000D: stloc.0 // array 

特别注意第三行。 该操作代码是产生转换的指令,并在失败时抛出exception。