隐式转换0到枚举

时间:2019-05-23 08:54:05

标签: c# enums type-conversion implicit-conversion

在C#中,可以将十进制文字0隐式转换为枚举(或其基础类型为枚举的可空值)。

C# spec,GitHub上的当前版本

  

隐式枚举转换   允许将decimal_integer_literal 0转换为任何enum_type   以及其基础类型为enum_type的任何nullable_type。在里面   在后一种情况下,通过转换为   基本的enum_type并包装结果(可为null的类型)。

ECMA-334,第11.2.4节隐式枚举转换

  

隐式枚举转换允许使用十进制整数   0(或0L等)将转换为任何枚举类型和任何   nullable-value-type,其基础类型是枚举类型。在里面   在后一种情况下,通过转换为   基础枚举类型并包装结果(第9.3.11节)

基于此,以下所有示例均应合法。此示例来自Eric Lippert的文章The Root Of All Evil, Part One

enum E
{
  X, Y, Z
}

E e1 = E.X;
E e2 = 0 | E.X;
E e3 = E.X | 0;
E e4 = E.X | 0 | 0;
E e5 = 0 | E.X | 0;

但是,正如Eric所解释的,以下情况应该是非法的:

E e6 = 0 | 0 | E.X;

原因是0 | 0 | E.X(0 | 0) | E.X相同,而0 | 0不是文字,而是编译时常数,值为0。以下情况:

E e7 = 1 - 1;
E e8 = 2 - 1 - 1 + 0;
E e9 = (0L & 1);

但是,所有这些都可以正常工作;在此示例中,e6e7e8e9的值为E.X

那是为什么?标准中是否有一个(较新的)规范,说明编译时常数为0也可以隐式转换为任何枚举,或者这是编译器在没有完全遵循规范的情况下执行的操作?

1 个答案:

答案 0 :(得分:3)

您已经注意到,0 | 0 | E.X被绑定为(0 | 0) | E.X

Eric指出编译器未遵循0 | 0 | E.X的规范:

  

在获得完整的解析树之后,我们遍历解析树,以确保所有类型都能正常工作。不幸的是,初始类型绑定通过非常奇怪地进行了算法优化。它检测到0 | something并积极地将其替换为0 | something,就编译器而言,第六种情况与第二种情况相同,这是合法的。啊!

评论中的埃里克笔记:

  

但是(7-7)| E.X确实会产生错误

似乎Roslyn在折叠常量方面比本机编译器要聪明一些。他们很可能在这里着眼于效率,而不必担心在极端情况下保留逐个bug的行为。

出于相同的原因,现在似乎完全相同的问题似乎适用于7 - 7或编译器在初始类型绑定过程中可以评估为0的任何其他表达式。

我认为不断发生折叠here

newValue = FoldNeverOverflowBinaryOperators(kind, valueLeft, valueRight);
if (newValue != null)
{
    return ConstantValue.Create(newValue, resultType);
}

如您所见,这将创建一个新的ConstantValue。因此(0 | 0) | E.X会被折叠成0 | E.X,其中第一个0是常数。当编译器折叠0 | E.X时,它不知道0不是原始源中的文字0,而是编译器生成的常量,因此折叠就像您最初写过0 | E.X一样。

您的其他示例也是如此,我认为这是通过相同的代码完成的。 1 - 1与其他变量一样被折叠成常数0。编译器可以在编译时将其评估为0的任何表达式都会发生这种情况。