Why can I assign 0.0 to enumeration values, but not 1.0

Just out of curiosity: why can I assign 0.0 to a variable that has an enum type but not 1.0? Take a look at the following code:

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 } } 

I thought conversions between numerical types and enum values ​​are only allowed with throws? That is, I could write Foo value2 = (Foo) 1.0; so line 2 in Main can compile. Why is there an exception for a value of 0.0 in C #?

+84
c #
May 20 '14 at 14:24
source share
4 answers

This is a bug you can use 0.0. The compiler implicitly processes all constant expressions with a null value as 0.

Now it’s right for the compiler to allow implicit conversion from the constant expression int 0 to your enum in accordance with section 6.1.3 of the C # 5 specification:

Implicit conversion of enumerations allows you to convert a decimal literal integer 0 to any type of enumeration and any type with a null type, the base type of which is an enumeration type. In the latter case, the conversion is evaluated by converting to the base type of enumeration and wrapping the result (§4.1.10).

I spoke with the C # team about this before: they would like to remove the random conversion from 0.0 (and indeed 0.0m and 0.0f) to enum values, but unfortunately I realized that it broke too much code - although it never should have been allowed first.

The Mono mcs compiler disallows all these floating point conversions, although this allows:

 const int Zero = 0; ... SomeEnum x = Zero; 

even though Zero is a constant expression, but not a decimal integer literal.

I would not be surprised to see a change to the C # specification in the future to allow any integer constant expression with a value of 0 (i.e. mimic mcs ), but I would not expect floating point conversions to ever be officially correct. (I was mistaken earlier to predict the future of C #, of course ...)

+91
May 20 '14 at 14:28
source share

John's answer is correct. I would add the following points to this.

  • I caused this stupid and embarrassing mistake. A lot of apologies.

  • The error was caused by a misunderstanding of the semantics of the predicate "expression is zero" in the compiler; I figured he only checked for integer zero, when in fact he checked more on the lines of "is this the default value of this type?" In fact, in an earlier version of the error it was really possible to assign a default value of any type to an enumeration! Now these are the default values ​​only by default. (Lesson: Carefully identify your assistant’s predicates.)

  • The behavior I was trying to implement, I messed up, was actually a workaround for a slightly different error. Here you can read the whole scary story: http://blogs.msdn.com/b/ericlippert/archive/2006/03/28/the-root-of-all-evil-part-one.aspx and here http: / /blogs.msdn.com/b/ericlippert/archive/2006/03/29/the-root-of-all-evil-part-two.aspx (Lesson: it is very easy to introduce new worst errors when installing old ones.)

  • The C # team decided to fix this erroneous behavior, and not fix it, because the risk of breaking existing code without any convincing advantages was too high. (Lesson: do it right the first time!)

  • The code I wrote in Roslyn to preserve this behavior can be found in the HasImplicitEnumerationConversion method in compilers\csharp\source\binder\semantics\conversions\conversions.cs - see more details on what Roslin's behavior is. (Note that I took a lesson in naming your predicates - HasImplicitEnumerationConversion , IsNumericType and IsConstantNumericZero all do exactly what they say in tins. I wrote almost all the code in the Conversions directory, I recommend that you read it all, as there are many interesting facts about how C # deviates from the specification in the comments I have decorated each of them with SPEC VIOLATION to facilitate their search.)

Another interesting topic: C # also allows you to use any enum value in an enumeration initializer, regardless of its nullness:

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

The specification is somewhat vague as to whether this should be legal or not, but again, since this has been in the compiler for a long time, new compilers are likely to support the behavior.

+94
May 20 '14 at 20:39
source share

Enumerations in C # are, by definition, integral values. For consistency, C # should not accept one of these assignments, but 0.0 silently perceived as integral 0 . This is probably a limitation with C, where the literal 0 specially processed and could essentially take any given type - an integer, a floating-point number, a null pointer ... you name it.

+10
May 20 '14 at 14:26
source share

enum is really intended (in all languages ​​that support it) to be a way to work with meaningful and unique strings (labels), rather than with numeric values. Thus, in your example, you should use Bar and Baz when working with the Foo enumerated data type. You should never use (compare or assign) an integer, even if many compilers allow you to leave with it (the enumerations are usually internal integers), in which case 0.0 is thoughtlessly regarded as 0 by the compiler.

It is clear that it would be correct to add the integer n to the numeric value to get n values ​​further down the line or take val2-val1 to see how far they are from each other, but if the language specification clearly does not allow this, I would avoid this . (Think that the listed value looks like a C pointer, in how you can use it.) It makes no sense to list the reasons with floating point numbers and fixed increment between them, but I have not heard about this being done in any language.

+3
May 20 '14 at 17:13
source share



All Articles