在C#中,为什么单个强制转换同时执行拆箱和枚举转换?

Jep*_*sen 9 c# enums boxing specifications type-safety

通常,人们会期望并希望首先取消装箱值类型然后执行某种值类型转换为另一种值类型需要两次转换.这是一个例子:

  // create boxed int
  IFormattable box = 42;       // box.GetType() == typeof(int)


  // unbox and narrow
  short x1 = (short)box;       // fails runtime :-)
  short x2 = (short)(int)box;  // OK

  // unbox and make unsigned
  uint y1 = (uint)box;         // fails runtime :-)
  uint y2 = (uint)(int)box;    // OK

  // unbox and widen
  long z1 = (long)box;         // fails runtime :-)
  long z2 = (long)(int)box;    // OK (cast to long could be made implicit)
Run Code Online (Sandbox Code Playgroud)

正如你从我的表情中看到的那样,如果我只使用一个演员,我很高兴这些转换会失败.毕竟,尝试在一次操作中将值类型拆分为不同的值类型可能是编码错误.

(IFormattable界面没有什么特别之处; object如果您愿意,也可以使用该课程.)

然而,今天我意识到这与枚举不同(当(并且仅当)枚举具有相同的底层类型时).这是一个例子:

  // create boxed DayOfWeek
  IFormattable box = DayOfWeek.Monday;    // box.GetType() == typeof(DayOfWeek)


  // unbox and convert to other
  // enum type in one cast
  DateTimeKind dtk = (DateTimeKind)box;   // succeeds runtime :-(

  Console.WriteLine(box);  // writes Monday
  Console.WriteLine(dtk);  // writes Utc
Run Code Online (Sandbox Code Playgroud)

我认为这种行为是不幸的.它应该是强制性的(DateTimeKind)(DayOfWeek)box.阅读C#规范,我认为数字转换和枚举转换之间没有任何理由.在这种情况下,感觉类型安全性丢失了.

您是否认为这是"未指定的行为",可以在未来的.NET版本中进行改进(无需更改规范)?这将是一个突破性的变化.

此外,如果任一枚举类型的(无论是供应商DayOfWeek还是DateTimeKind在我的例子),决定从改变底层类型的枚举类型中的一种int别的东西(可能是long,short...),然后一下子的单播代码将停止工作,这似乎很愚蠢.

当然,枚举DayOfWeekDateTimeKind不特殊.这些可以是任何枚举类型,包括用户定义的类型.

有点相关:为什么取消装箱枚举会产生奇怪的结果?(将其int直接打开到枚举中)

加成:

好的,这么多的答案和评论都集中在如何对"枚举"进行处理.虽然这本身很有趣,但我想更多地关注C#规范是否涵盖了观察到的行为.

假设我写了这个类型:

struct YellowInteger
{
  public readonly int Value;

  public YellowInteger(int value)
  {
    Value = value;
  }

  // Clearly a yellow integer is completely different
  // from an integer without any particular color,
  // so it is important that this conversion is
  // explicit
  public static explicit operator int(YellowInteger yi)
  {
    return yi.Value;
  }
}
Run Code Online (Sandbox Code Playgroud)

然后说:

object box = new YellowInteger(1);
int x = (int)box;
Run Code Online (Sandbox Code Playgroud)

那么,C#规范是否会说明这是否会在运行时成功?对于我所关心的一切,.NET可能会将a YellowInteger视为Int32具有不同类型元数据(或其他任何类型的元数据),但任何人都可以保证.NET不会"混淆"a YellowIntegerInt32拆箱时?那么在C#规范中哪里可以看到是否(int)box会成功(调用我的显式运算符方法)?

dob*_*lak 5

当你使用:

IFormattable box = 42; 
long z2 = (long)(int)box;
Run Code Online (Sandbox Code Playgroud)

你实际上是拆箱然后再投射.

但在你的第二个案件中:

IFormattable box = DayOfWeek.Monday; 
DateTimeKind dtk = (DateTimeKind)box;
Run Code Online (Sandbox Code Playgroud)

你根本不进行任何演员表演.您只需取消装箱值即可.枚举元素的默认基础类型是int.

更新以引用真实问题:

您在评论中提到的规范:

The explicit enumeration conversions are:
...
From any enum-type to any other enum-type.
Run Code Online (Sandbox Code Playgroud)

这实际上是正确的.我们不能隐含地转换:

//doesn't compile
DateTimeKind dtk = DayOfWeek.Monday;
Run Code Online (Sandbox Code Playgroud)

但我们可以明确转换:

DateTimeKind dtk = (DateTimeKind)DayOfWeek.Monday;
Run Code Online (Sandbox Code Playgroud)

你似乎找到了一个仍然需要它的情况.但是当与拆箱结合使用时,只需要指定显式转换,并且可以省略拆箱.

更新2

有一种感觉,有人必须注意到之前,去过Google,搜索"拆箱转换枚举"并猜测是什么?Skeet在2005年发表了关于它的博客(CLI规范错误与拆箱和枚举)