c#

c# - ¿Por qué puedo asignar 0.0 a los valores de enumeración, pero no 1.0



(4)

Solo por curiosidad: ¿por qué puedo asignar 0.0 a una variable que es de tipo de enumeración, pero no 1.0? Echa un vistazo al siguiente código:

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

Pensé que las conversiones entre los tipos numéricos y los valores de enumeración solo se permiten a través de los moldes. Es decir, podría escribir Foo value2 = (Foo) 1.0; para que la línea 2 en Main pueda compilarse. ¿Por qué hay una excepción para el valor 0.0 en C #?


Es un error que puedes usar 0.0. El compilador trata implícitamente todas las expresiones constantes con un valor de cero como solo 0.

Ahora, es correcto que el compilador permita una conversión implícita de una expresión int constante de 0 a su enumeración según la sección 6.1.3 de la especificación C # 5:

Una conversión de enumeración implícita permite que el decimal-entero-literal 0 se convierta en cualquier tipo de enumeración y para cualquier tipo anulable cuyo tipo subyacente sea un tipo enum. En el último caso, la conversión se evalúa convirtiendo al tipo de enumeración subyacente y ajustando el resultado (§4.1.10).

He hablado con el equipo de C # sobre esto antes: les hubiera gustado haber eliminado la conversión accidental de 0.0 (y de hecho 0.0m y 0.0f) a los valores enum, pero desafortunadamente descubrí que rompió demasiados códigos, aunque nunca debería haber sido permitido en primer lugar.

El compilador Mono mcs prohíbe todas estas conversiones de coma flotante, aunque permite:

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

a pesar del hecho de que Zero es una expresión constante, pero no un decimal-entero-literal.

No me sorprendería ver que la especificación C # cambia en el futuro para permitir cualquier expresión constante entera con un valor de 0 (es decir, para imitar mcs ), pero no esperaría que las conversiones de coma flotante fueran oficialmente correctas. (He estado equivocado antes de predecir el futuro de C #, por supuesto ...)


La respuesta de Jon es correcta. Yo agregaría los siguientes puntos.

  • Yo causé este error tonto y embarazoso. Muchas disculpas.

  • El error fue causado por mí al malinterpretar la semántica de un predicado de "expresión es cero" en el compilador; Creí que solo estaba comprobando la igualdad cero entera, cuando en realidad estaba buscando más en la línea de "¿es este el valor predeterminado de este tipo?" De hecho, en una versión anterior del error, en realidad era posible asignar el valor predeterminado de cualquier tipo a una enumeración. Ahora solo son valores predeterminados de los números. (Lección: Nombra a tu ayudante predicado cuidadosamente.)

  • El comportamiento que estaba tratando de implementar que arruiné fue en realidad una solución para un error ligeramente diferente. Puede leer toda la historia terrible aquí: http://blogs.msdn.com/b/ericlippert/archive/2006/03/28/the-root-of-all-evil-part-one.aspx y aquí http://blogs.msdn.com/b/ericlippert/archive/2006/03/29/the-root-of-all-evil-part-two.aspx (Lección: es muy fácil introducir nuevos errores más graves mientras se soluciona el problema anterior) unos)

  • El equipo de C # decidió consagrar este comportamiento defectuoso en lugar de corregirlo porque el riesgo de romper el código existente sin ningún beneficio convincente era demasiado alto. (Lección: ¡hazlo bien la primera vez!)

  • El código que escribí en Roslyn para preservar este comportamiento se puede encontrar en el método HasImplicitEnumerationConversion en compilers/csharp/source/binder/semantics/conversions/conversions.cs para obtener más detalles sobre el comportamiento de Roslyn. (Tenga en cuenta que tomé la lección de nombrar bien a sus predicados - HasImplicitEnumerationConversion , IsNumericType y IsConstantNumericZero hacen exactamente lo que dicen en el estaño. Escribí casi todo el código en el directorio de Conversiones; lo animo a que lo lea todo ya que hay muchos hechos interesantes acerca de cómo C # se aparta de la especificación en los comentarios. Adorné cada uno con SPEC VIOLATION para que sean fáciles de encontrar.)

Un punto más de interés: C # también permite que cualquier valor enum sea ​​utilizado en un inicializador de enumeración sin importar su zeroness:

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

La especificación es algo vaga en cuanto a si esto debería ser legal o no, pero una vez más, como esto ha estado en el compilador durante mucho tiempo, es probable que los nuevos compiladores mantengan el comportamiento.


Las enumeraciones en C # son, por definición, valores integrales. Para consistencia, C # no debería aceptar ninguna de estas asignaciones, pero 0.0 se trata en silencio como integral 0 . Esto es probablemente un remanente de C, donde el literal 0 fue tratado especialmente y podría tomar cualquier tipo dado: entero, número de punto flotante, puntero nulo ... lo que sea.


enum está realmente destinado (en todos los idiomas que lo admiten) a ser una forma de trabajar con cadenas (etiquetas) significativas y únicas en lugar de valores numéricos. Por lo tanto, en su ejemplo, solo debería utilizar Bar y Baz cuando se trata de un tipo de datos enumerados Foo . Nunca debe usar (comparar o asignar) un número entero, aunque muchos compiladores le permitirán salirse con la suya (las enumeraciones suelen ser números enteros internamente), y en este caso, el compilador trata descuidadamente un 0 como un 0.

Conceptualmente, debería estar bien agregar un entero n a un valor enumerado, para obtener n valores más adelante en la línea, o tomar val2 - val1 para ver qué tan separados están, pero a menos que la especificación del lenguaje lo permita explícitamente, lo evitaré. (Piense en un valor enumerado como si fuera un puntero C, en la forma en que puede usarlo). No hay razón para que las enumeraciones no se puedan implementar con números de punto flotante, y un incremento fijo entre ellos, pero no he oído hablar de esto se hace en cualquier idioma.