虽然使用具有多个重载但需要不同数据类型但没有枚举的外部API,我决定创建一个方便的方法,为枚举提供更多的类型安全性,最后得到如下内容:
namespace TestEnumPromotion
{
enum Values
{
Value0,
Value1,
}
class Program
{
static void Main(string[] args)
{
// Prints int
Overloaded(0);
// Prints Values
Overloaded(Values.Value0);
// Does not compile! :-/
Overloaded((byte) 0);
// Prints int
byte b = 0;
Overloaded(b);
}
static void Overloaded(int i)
{
Console.WriteLine("int");
}
static void Overloaded(Values i)
{
Console.WriteLine("Values");
}
}
}
但我很惊讶地看到代码无法编译,因为Overloaded((byte) 0)
:
以下方法或属性之间的调用不明确:'Program.Overloaded(int)'和'Program.Overloaded(Values)'
但byte
无法自动提升为Values
,Values v = (byte)b
将无法编译,因为:
无法将类型'byte'隐式转换为'`TestEnumPromotion.Values`'。
所以唯一可能的重载应该是int,对吗?
我认为可能枚举只是语法糖而且编译器会生成接收int的方法,但是通过ILDASM查看IL会显示实际创建了一个接受枚举的方法。
.method private hidebysig static void Overloaded(valuetype TestEnumPromotion.Values i) cil managed
{
// Code size 13 (0xd)
.maxstack 8
IL_0000: nop
IL_0001: ldstr "Values"
IL_0006: call void [mscorlib]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: ret
} // end of method Program::Overloaded
发生了什么事?
答案 0 :(得分:4)
这是语言中一个奇怪的小规则的结果:文字0
可以隐式转换为任何enum
。
UPDATE :根据评论和链接的来源,编译器实际上并不遵循规范。编译器允许隐式转换任何零常量,而不仅仅是文字零。您可以阅读有关此问题的更多信息here。
这不会编译:
Values someValue = 1; //can not implicitly convert `int` to...
但是,有趣的是,这将是:
Values someValue = 0;
在您的情况下,因为(byte)0
(常数零)可隐式转换为Values
和int
,编译器无法选择最佳重载并且无法解析调用。如果您将代码更改为(byte)1
或任何其他文字值,它将编译得很好。
值得一提的是Overloaded(0)
工作的原因仅仅是因为编译器找到了完全匹配;不需要隐式演员,所以Overloaded(int i)
赢得无争议。
引用c#4.0规范:
1.10(...)任何枚举类型的默认值是转换为枚举类型的整数值零。在变量自动初始化为默认值的情况下,这是给予枚举类型变量的值。 为了使枚举类型的默认值易于使用,文字0隐式转换为任何枚举类型。(...)