为什么我可以将0.0分配给枚举值,而不是1.0

时间:2014-05-20 14:24:12

标签: c#

出于好奇:为什么我可以将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有例外?

4 个答案:

答案 0 :(得分:96)

Jon的回答是正确的。我想补充以下几点。

  • 我造成了这个愚蠢而令人尴尬的错误。很多道歉。

  • 错误是由于我误解了编译器中“表达式为零”谓词的语义;我相信它只检查整数零相等,实际上它正在检查更多的“这是这种类型的默认值吗?”事实上,在早期版本的bug中,实际上可以将任何类型的默认值分配给枚举!它现在只是数字的默认值。 (课程:仔细命名助手谓词。)

  • 我试图实现的行为,我搞砸了实际上是一个稍微不同的bug的解决方法。你可以在这里阅读整个可怕的故事:http://blogs.msdn.com/b/ericlippert/archive/2006/03/28/the-root-of-all-evil-part-one.aspxhttp://blogs.msdn.com/b/ericlippert/archive/2006/03/29/the-root-of-all-evil-part-two.aspx(教训:在修复旧错误时,很容易引入新的更糟糕的错误。)

  • C#团队决定将这种错误行为归咎于而不是修复它,因为破坏现有代码而没有引人注目的好处的风险太高了。 (课程:第一次就把它弄好!)

  • 我在Roslyn中编写的用于保存此行为的代码可以在HasImplicitEnumerationConversion中的方法compilers\csharp\source\binder\semantics\conversions\conversions.cs中找到 - 有关Roslyn行为究竟是什么的详细信息,请参阅它。 (请注意,我接受了将谓词命名的教训 - HasImplicitEnumerationConversionIsNumericTypeIsConstantNumericZero都完全按照他们在锡上所说的内容。我写了几乎所有的代码转换目录;我鼓励您阅读所有内容,因为有很多有趣的事实,关于C#如何与评论中的规范不同。我使用SPEC VIOLATION进行装饰,以便于查找。)

还有一个兴趣点:C#还允许在枚举初始值设定项中使用任何枚举值,无论其是否为zeroness:

enum E { A = 1 }
enum F { B = E.A }  // ???

关于这是否合法,规范有些模糊,但同样,由于这已经在编译器中存在了很长时间,新的编译器可能会保持这种行为。

答案 1 :(得分:94)

这是一个可以使用0.0的错误。编译器隐式将所有值为零的常量表达式视为0。

现在,编译器的正确允许从常量int表达式0到您的枚举的隐式转换,如C#5规范的第6.1.3节所述:

  

隐式枚举转换允许将decimal-integer-literal 0转换为任何枚举类型和任何可以为其类型为枚举类型的nullable类型。在后一种情况下,通过转换为基础枚举类型并包装结果来评估转换(§4.1.10)。

之前我曾与C#团队讨论过这个问题:他们希望将意外转换从0.0(实际上是0.0m和0.0f)转换为枚举值,但不幸的是我认为它破坏了太多的代码 - 尽管它本来就不应该被允许。

Mono mcs编译器禁止所有这些浮点转换,尽管 允许:

const int Zero = 0;
...

SomeEnum x = Zero;

尽管Zero是一个常量表达式,但不是十进制整数文字。

我不会惊讶地看到C#规范将来会改变以允许任何值为0的整数常量表达式(即模仿mcs),但我不希望浮点转换永远正式是正确的。 (关于预测C#的未来,我当然错了,当然......)

答案 2 :(得分:10)

C#中的枚举按定义为整数值。为了保持一致性,C#不应接受这些分配中的任何一个,但0.0会被默认视为整数0。这可能是C的延续,其中文字0被特别处理,并且基本上可以采用任何给定的类型 - 整数,浮点数,空指针......你可以命名它。

答案 3 :(得分:3)

enum 真正意图(在所有支持它的语言中)是一种处理有意义且唯一的字符串(标签)而不是数值的方法。因此,在您的示例中,在处理 Foo 枚举数据类型时,您应该只使用 Bar Baz 。你永远不应该使用(比较或分配)一个整数,即使许多编译器会让你逃脱它(枚举通常整数内部),在这种情况下,0.0被不小心对待编译器为0。

从概念上讲,将一个整数 n 添加到枚举值,将 n 值添加到该行的下方,或者取 val2应该是可以的 - val1 看看它们相隔多远,但除非语言规范明确允许,否则我会避免它。 (将枚举值视为C指针,以你可以使用它的方式。)没有理由使用浮点数实现枚举,并且它们之间有固定的增量,但我没有听说过这是用任何语言完成的。