通常,人们会期望并希望,首先需要两次强制转换才能将值类型拆箱,然后执行某种值类型转换为另一种值类型。这是一个例子:
// 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)
正如您从我的笑脸中看到的那样,如果我只使用一个演员表,我很高兴这些转换将失败。毕竟,尝试在一次操作中将值类型拆箱为不同的值类型可能是一个编码错误。
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
我认为这种行为是不幸的。真的应该说是必须的(DateTimeKind)(DayOfWeek)box
。阅读 C# 规范,我看不出数字转换和枚举转换之间存在这种差异的理由。在这种情况下,感觉就像类型安全丢失了。
您是否认为这是“未指定的行为”,可以在未来的 .NET 版本中改进(无需更改规范)?这将是一个突破性的变化。
此外,如果任一枚举类型(DayOfWeek
或DateTimeKind
在我的示例中)的供应商决定将其中一个枚举类型的基础类型从其他类型更改为int
其他类型(可能是long
, short
, ...),那么突然之间上面的一次性代码将停止工作,这看起来很愚蠢。
当然,枚举DayOfWeek
并DateTimeKind
没有什么特别之处。这些可以是任何枚举类型,包括用户定义的类型。
有点相关:为什么拆箱枚举会产生奇怪的结果?(将 anint
直接拆箱到枚举中)
添加:
好的,很多答案和评论都集中在如何“在幕后”处理枚举。虽然这本身很有趣,但我想更多地关注观察到的行为是否包含在 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;
}
}
然后说:
object box = new YellowInteger(1);
int x = (int)box;
那么,C# 规范是否说明这是否会在运行时成功?就我而言,.NET 可能会将 aYellowInteger
视为具有不同类型元数据(或其他任何名称)的 a,但是任何人都可以保证 .NET在拆箱时Int32
不会“混淆” aYellowInteger
和 a吗?Int32
那么我可以在 C# 规范中的哪个位置查看是否(int)box
会成功(调用我的显式运算符方法)?