为什么我可以给枚举值赋值0.0,而不能赋值1.0 ?
本文关键字:赋值 不能 我可以 枚举 为什么 | 更新日期: 2023-09-27 18:04:19
只是出于好奇:为什么我可以将0.0赋值给一个枚举类型的变量,而不是1.0?看看下面的代码:
public enum Foo
{
Bar,
Baz
}
class Program
{
static void Main()
{
Foo value1 = 0.0;
Foo value2 = 1.0; // This line does not compile
Foo value3 = 4.2; // This line does not compile
}
}
我认为数值类型和枚举值之间的转换只允许通过强制类型转换?也就是说,我可以写Foo value2 = (Foo) 1.0;
,以便Main
中的第2行可以编译。为什么在c#中有一个异常值0.0
?
Jon的答案正确。我想补充以下几点:
-
我造成了这个愚蠢和尴尬的错误。许多道歉。
-
这个bug是由于我误解了"表达式是零"的语义而导致的。编译器中的谓词;我以为它只检查整数0相等,而实际上它在检查更多的"这是该类型的默认值吗?"事实上,在该错误的早期版本中,实际上可以将任何类型的默认值赋值给enum!现在只有默认值的数字。(教训:仔细命名你的辅助谓词)
-
我试图实现的行为,我搞砸了实际上是一个稍微不同的错误的解决方案。你可以在这里阅读整个可怕的故事:https://learn.microsoft.com/en-us/archive/blogs/ericlippert/the-root-of-all-evil-part-one和https://learn.microsoft.com/en-us/archive/blogs/ericlippert/the-root-of-all-evil-part-two(教训:在修复旧的bug时,很容易引入新的更严重的bug。)
-
c#团队决定保留这个有bug的行为,而不是修复它,因为没有引人注目的好处而破坏现有代码的风险太高了。(教训:第一次就做对!)
-
我在Roslyn中编写的代码可以在https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs中的方法
IsConstantNumericZero
中找到,以了解Roslyn行为的更多细节。我在Conversions目录中编写了几乎所有的代码;我鼓励你阅读所有的内容,因为在注释中有许多关于c#如何偏离规范的有趣事实。我用SPEC VIOLATION来装饰它们,使它们更容易找到。
enum E { A = 1 }
enum F { B = E.A } // ???
规范对于这是否应该合法有些模糊,但是,由于这在编译器中已经存在很长时间了,新的编译器可能会保持这种行为。
你可以使用0.0的一个bug。编译器隐式地将所有值为0的常量表达式视为0。
现在,它是正确的编译器允许从常量int
表达式0隐式转换为您的枚举,根据c# 5规范的第6.1.3节:
我曾经和c#团队讨论过这个问题:他们希望删除意外的从0.0(实际上是0.0m和0.0f)到enum值的转换,但不幸的是,我收集到它破坏了太多的代码——即使它一开始就不应该被允许。隐式枚举转换允许将十进制整型字面值0转换为任何枚举类型和其基础类型为枚举类型的任何可空类型。在后一种情况下,通过转换为基础的enum类型并包装结果来计算转换(第4.1.10节)。
Mono mcs
编译器禁止所有这些浮点转换,尽管允许:
const int Zero = 0;
...
SomeEnum x = Zero;
,尽管Zero
是一个常量表达式,但不是一个小数整型文字。
mcs
),我不会感到惊讶,但我不会期望浮点数转换正式是正确的。(当然,我之前预测c#的未来是错误的……)
c#中的枚举根据定义是整数值。为了一致性,c#不应该接受这两种赋值,但是0.0
被静默地当作整型的0
来处理。这可能是C语言的延续,在C语言中,文字0
被特殊处理,基本上可以接受任何给定的类型——整数、浮点数、空指针……你能想到的。
enum实际上(在所有支持它的语言中)是一种处理有意义且唯一的字符串(标签)而不是数值的方法。因此,在您的示例中,当处理Foo枚举数据类型时,您应该只使用Bar和Baz。你永远不应该使用(比较或赋值)整数,即使许多编译器允许你这样做(枚举在内部通常是整数),在这种情况下,0.0被编译器粗心地当作0处理。
从概念上讲,将整数n添加到枚举值中应该是正确的,以便在行中得到n值,或者取val2-val1以查看它们之间的距离,但除非语言规范明确允许这样做,否则我将避免这样做。(可以把枚举值想象成C指针,就像您可以使用它的方式一样。)没有理由不能用浮点数和它们之间的固定增量来实现枚举,但我还没有听说过在任何语言中这样做。