0 到枚举的隐式转换
Implicit conversion of 0 to enums
在 C# 中,十进制文字 0
可以隐式转换为枚举(或基础类型为枚举的可空对象)。
C# spec,当前版本 GitHub
An implicit enumeration conversion
permits the decimal_integer_literal 0 to be converted to any enum_type
and to any nullable_type whose underlying type is an enum_type. In the
latter case the conversion is evaluated by converting to the
underlying enum_type and wrapping the result (Nullable types).
ECMA-334,第 11.2.4 节隐式枚举转换
An implicit enumeration conversion permits the decimal-integer-literal
0 (or 0L, etc.) to be converted to any enum-type and to any
nullable-value-type whose underlying type is an enum-type. In the
latter case the conversion is evaluated by converting to the
underlying enum-type and wrapping the result (§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);
然而,这些都很好;本例中的 e6
、e7
、e8
和 e9
的值为 E.X
.
这是为什么?标准中是否有一个(更新的)规范说编译时常量为 0 也可以隐式转换为任何枚举,或者这是编译器在不完全遵循规范的情况下所做的事情?
如您所述,0 | 0 | E.X
被绑定为 (0 | 0) | E.X
。
Eric 注意到编译器不遵循 0 | 0 | E.X
:
的规范
After we've got a complete parse tree we walk through the parse tree making sure that all the types work out. Unfortunately the initial type binding pass bizarrely enough does arithmetic optimizations. It detects the 0|something and aggressively replaces it with just something , so as far as the compiler is concerned, the sixth case is the same as the second, which is legal. Argh!
埃里克在评论中指出:
but (7-7)|E.X does correctly produce an error
看起来 Roslyn 在折叠常量方面比本机编译器更聪明。很可能他们的目标是提高效率,而不关心在 edge-case.
中保留 bug-for-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
,而是一个 compiler-generated 常量,并且所以把它折叠起来,就好像你最初写的是 0 | E.X
。
您的其他示例也发生了完全相同的情况,我认为这是通过相同的代码完成的。 1 - 1
和其他值一样被折叠成常量 0
。对于编译器可以在 compile-time.
处评估为 0
的任何表达式,都会发生这种情况
在 C# 中,十进制文字 0
可以隐式转换为枚举(或基础类型为枚举的可空对象)。
C# spec,当前版本 GitHub
An implicit enumeration conversion permits the decimal_integer_literal 0 to be converted to any enum_type and to any nullable_type whose underlying type is an enum_type. In the latter case the conversion is evaluated by converting to the underlying enum_type and wrapping the result (Nullable types).
ECMA-334,第 11.2.4 节隐式枚举转换
An implicit enumeration conversion permits the decimal-integer-literal 0 (or 0L, etc.) to be converted to any enum-type and to any nullable-value-type whose underlying type is an enum-type. In the latter case the conversion is evaluated by converting to the underlying enum-type and wrapping the result (§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);
然而,这些都很好;本例中的 e6
、e7
、e8
和 e9
的值为 E.X
.
这是为什么?标准中是否有一个(更新的)规范说编译时常量为 0 也可以隐式转换为任何枚举,或者这是编译器在不完全遵循规范的情况下所做的事情?
如您所述,0 | 0 | E.X
被绑定为 (0 | 0) | E.X
。
Eric 注意到编译器不遵循 0 | 0 | E.X
:
After we've got a complete parse tree we walk through the parse tree making sure that all the types work out. Unfortunately the initial type binding pass bizarrely enough does arithmetic optimizations. It detects the 0|something and aggressively replaces it with just something , so as far as the compiler is concerned, the sixth case is the same as the second, which is legal. Argh!
埃里克在评论中指出:
but (7-7)|E.X does correctly produce an error
看起来 Roslyn 在折叠常量方面比本机编译器更聪明。很可能他们的目标是提高效率,而不关心在 edge-case.
中保留 bug-for-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
,而是一个 compiler-generated 常量,并且所以把它折叠起来,就好像你最初写的是 0 | E.X
。
您的其他示例也发生了完全相同的情况,我认为这是通过相同的代码完成的。 1 - 1
和其他值一样被折叠成常量 0
。对于编译器可以在 compile-time.
0
的任何表达式,都会发生这种情况