为啥将数组拆箱为不正确的类型不会在 C# 中引发异常? [复制]
Posted
技术标签:
【中文标题】为啥将数组拆箱为不正确的类型不会在 C# 中引发异常? [复制]【英文标题】:Why does unboxing array to an incorrect type not throw an exception in C#? [duplicate]为什么将数组拆箱为不正确的类型不会在 C# 中引发异常? [复制] 【发布时间】:2021-11-24 22:03:55 【问题描述】:值和数组的转换和拆箱存在奇怪的不一致:
var us = new uint[] 1, 2, 3 ;
var i1 = (int)us[0]; // direct value to value: works
var i2 = (int)(object)us[0]; // unboxing value to value: compiles, throws at runtime
var is1 = (int[])u; // direct array to array: does not compile
var is2 = (int[])(object)u; // unboxing array to array: works!
is2.GetType().Name // UInt32[]
为什么在禁止直接转换的情况下允许数组拆箱?
【问题讨论】:
这基本上是 C# 语言规则允许的内容和 CLR 允许的内容之间的区别。这是一个不幸的阻抗不匹配:( 它不是uint
大于int
因此它不应该工作,因为目前编译器没有每个成员的值
同意@JonSkeet。请注意,您可以举一个更糟糕的例子。比如,将us
的定义更改为new unit[] 3000000001u, 3000000002u, 3000000003u
(选择的数字介于2**31
和2**32
之间,这样它们对于没有符号的32 位整数来说是可以的,但对于32 位有符号整数)。
也尝试阅读Why does "int[] is uint[] == true" in C# 和其他线程,看看相关的惊喜也源于不幸的问题,即 C# 语言和 .NET 运行时不同意 int[]
和 @ 987654331@是否兼容。
尝试更好的链接:Why does "int[] is uint[] == true" in C#
【参考方案1】:
为什么在禁止直接转换的情况下允许数组拆箱?
如果你做(int[])(object)us
,你就是在做两次转换,both of which are allowed by C#:
uint[]
到object
(您可以将任何引用类型转换为object
)
从object
到int[]
(您可以从object
转换为任何引用类型)
因此(int[])(object)us
编译。
如果您执行(int[])us
,您将执行从int[]
到uint[]
的单次转换,这是C# 不允许的。根据 C# 规范,如果 T
和 U
是引用类型,则只能从 T[]
转换为 U[]
,并且您可以从 T
转换为 U
。 int
和 uint
都不满足这两个条件,所以 (int[])us
不会编译。
在运行时,检查从uint[]
到int[]
的转换,看它是成功还是失败。 CLR 确实允许这样做。请参阅CLR spec 的第 I.8.7.1 节。
当且仅当以下至少一项成立时,签名类型 T 与签名类型 U 兼容。
[...]
T 是一个秩为 r 且元素类型为 V 的数组,U 是一个秩为 r 和元素类型为 W 相同的数组,V 是与 W 的数组元素兼容的数组。
[...]
签名类型 T 与签名类型 U 是数组元素兼容的,当且仅当 T 具有底层类型 V 并且 U 具有底层类型 W 并且:
V 与 W 兼容;或 V 和 W 具有相同的缩减类型。
而int
和uint
的“缩减类型”都是int
。
这就是(int[])(object)us
在运行时成功的原因。
另请注意,将us
转换为object
不是“装箱”。这里的一切都是引用类型。您只是在更改引用的类型。
【讨论】:
您可以使用类似以下的方法来(向下)强制转换对数组类型的引用,而不允许出现以下情况:(A)运行时类型和被强制转换的类型是数组类型整数,但一个是有符号的,另一个是无符号的。 (B) 这两种类型涉及整数类型和枚举类型,或者两种不同的枚举类型。 (C) 强制转换仅对 C# 的 病态数组协方差 和运行时有效(数组协方差是“病态的”,因为数组是读写的,而不是只读的,所以协方差是没有声音)。这里:static T[] PedanticDowncastToArray<T>(object obj) if (obj is T[] arr && arr.GetType() == typeof(T[])) return arr; throw new ArgumentException($"The argument 'obj' is not precisely an instance of a one-dimensional array with element type 'typeof(T)'.", nameof(obj));
以上是关于为啥将数组拆箱为不正确的类型不会在 C# 中引发异常? [复制]的主要内容,如果未能解决你的问题,请参考以下文章