为什么我可以将0.0赋给枚举值,而不是1.0
只是出于好奇:为什么我可以将0.0赋值给一个枚举types的variables,但不是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 } }
我认为数值types和枚举值之间的转换只允许通过转换? 那是我可以写Foo value2 = (Foo) 1.0;
所以Main
中的第2行可以编译。 为什么C#中的值为0.0
出现exception?
这是一个可以使用0.0的错误。 编译器隐含地将所有常数expression式的值都设为0。
现在,根据C#5规范的第6.1.3节,编译器允许从常量int
expression式0到您的枚举的隐式转换是正确的 :
隐式枚举转换允许将decimal-integer-literal 0转换为任何枚举types和任何基础types为枚举types的可空types。 在后一种情况下,通过转换为基础枚举types并包装结果来评估转换(第4.1.10节)。
之前我已经和C#团队谈过了:他们本来希望将0.0(实际上是0.0m和0.0f)的意外转换移除为枚举值,但是不幸的是我收集它打破了太多的代码 – 尽pipe它应该永远不会被允许摆在首位。
Mono mcs
编译器禁止所有这些浮点转换,尽pipe它允许:
const int Zero = 0; ... SomeEnum x = Zero;
尽pipeZero
是一个常量expression式,但不是十进制整数字面量。
如果将来C#规范发生变化,允许任何整数常量expression式的值为0(即模仿mcs
),我不会感到惊讶,但我不希望浮点数转换正确无误。 (当然,在我预测C#的未来之前,我错了)
Jon的回答是正确的。 我会补充以下几点。
-
我造成了这个愚蠢和尴尬的错误。 很多道歉。
-
这个错误是由于我误解了编译器中“expression式为零”谓语的语义造成的。 我相信它只是检查整数零相等,当它实际上是检查更多沿着“这是这种types的默认值?”的线。 事实上,在早期版本的bug中,实际上可以将任何types的默认值赋给枚举。 现在只有数字的默认值。 (教训:仔细命名你的助手谓词。)
-
我试图实现的行为,我搞砸了实际上是一个解决方法,稍微不同的错误。 你可以在这里阅读整个可怕的故事: http : //blogs.msdn.com/b/ericlippert/archive/2006/03/28/the-root-of-all-evil-part-one.aspx这里http: //blogs.msdn.com/b/ericlippert/archive/2006/03/29/the-root-of-all-evil-part-two.aspx (课程:在修复旧的问题时引入新的更糟的错误是非常容易的那些。)
-
C#团队决定提供这种bug的行为而不是修复它,因为破坏现有代码没有令人信服的好处的风险太高。 (课程:第一次就对了!)
-
我在Roslyn编写的代码保留了这种行为,可以在
HasImplicitEnumerationConversion
compilers\csharp\source\binder\semantics\conversions\conversions.cs
中的HasImplicitEnumerationConversion
方法中find – 详细了解Roslyn行为究竟是什么。 (请注意,我已经HasImplicitEnumerationConversion
将谓词命名为重点 –HasImplicitEnumerationConversion
,IsNumericType
和IsConstantNumericZero
完全按照他们的说法进行操作,我几乎将所有代码都写在了Conversions目录中,我鼓励您阅读所有的代码因为关于C#如何与注释中的规范分歧有许多有趣的事实,我用SPEC VIOLATION来装饰它们,以便于查找。)
还有一点值得关注:C#也允许在enum初始值设定项中使用任何枚举值 ,而不pipe它是否为zeroness:
enum E { A = 1 } enum F { B = EA } // ???
这个规范对于这个规范是否合法是有些模糊的,但是再次,由于这个在编译器中已经有很长一段时间了,所以新的编译器可能会保持这种行为。
C#中的枚举按定义是整数值。 为了一致性,C#不应该接受这些赋值,但是0.0
被默默地视为整数0
。 这可能是从C的保留,其中文字0
被特殊对待,可以实质上采取任何给定的types – 整数,浮点数,空指针…你的名字。
枚举是真正意图(支持它的所有语言)是一种方法来处理有意义和唯一的string(标签),而不是一个数值。 因此,在你的例子中,当处理一个Foo枚举数据types时,你应该只使用Bar和Baz 。 你不应该使用(比较或分配)整数,即使许多编译器会让你摆脱它(枚举通常是内部的整数),在这种情况下,编译器将0.0粗心地视为0。
从概念上讲,向枚举值添加一个整数n ,或者让val2 – val1看看它们有多远,应该没问题,但是除非语言规范明确地允许这个,否则我避免它。 (把枚举值看作像C指针一样,可以使用它。)没有理由使用浮点数来实现枚举,并且在它们之间有一个固定的增量,但是我没有听说过这是用任何语言来完成的。