为什么取消装箱枚举会产生奇怪的结果?

考虑以下::

Object box = 5; int @int = (int)box; // int = 5 int? nullableInt = box as int?; // nullableInt = 5; StringComparison @enum = (StringComparison)box; // enum = OrdinalIgnoreCase StringComparison? nullableEnum = box as StringComparison?; // nullableEnum = null. 

2件事::

  1. 为什么我可以拆箱到StringComparison ? 我想这是因为它的底层类型是Int32但我仍然发现它很奇怪。
  2. 为什么nullableEnum的值为null?

据我所知,唯一有效的拆箱是从盒装值类型到它的类型或可空类型。 如果int可以unbox到Enum ,那么为什么对于可空值可以不相同呢? 类似地,如果不是5个盒装的StringComparison.OrdinalIgnoreCase ,那么nullableInt将为null,但nullableEnum不会。

严格来说,我认为这是一个 错误 运行时的实现细节 ,因为C#规范说

如果源操作数为空,则取消装入可空类型会产生可空类型的空值,否则会将对象实例的解包装结果解析为可为空类型的基础类型。

也就是说,如果对StringComparison进行拆箱工作,那么取消装箱到Nullable 也应该有效。 有两点是否应该有效或两者都失败有点不清楚。 规范说

对于在给定的非可空值类型的取消装箱转换以在运行时成功,源操作数的值必须是对该非可空值类型的装箱值的引用。

您必须确定boxed int是否被认为是StringComparison类型的盒装值,因为StringComparison的基础类型是int。 规范继续说如果该框包含“不兼容的对象”,则抛出InvalidCastException。 int当然与StringComparison“兼容”,因为您可以安全地将堆中的四个字节复制到StringComparison变量中。

将枚举或整数强制转换为对象时,它仍保存类型信息。 所以box is StringComparison将返回false 。 但是允许将任何枚举或int转换为任何枚举,因此显式转换(StringComparison)box可以工作。 这是枚举的特例。 另一方面, Nullable只是一个普通的类,当你强制转换或检查类型时,T不会以任何特定的方式处理。 这就是为什么这段代码会抛出exception。

  StringComparison? nullableEnum = (StringComparison?)nullableInt; 

1)是的,枚举的基础类型是int,这就是它以这种方式工作的原因。 更。 你可以这样做:

 enum MyEnum { One = 1, Two = 2, } int i = 3; MyEnum myEnum = (MyEnum)i; // This works without exceptions. 

2)因为StringComparison? 实际上是Nullable ,它是不同的类型。 并且操作符仅检查对象是否与as运算符中指定的类型相同。