2012-03-04 8 views
9

No puedo entender por qué se compila el siguiente código.¿Por qué el compilador de C# no se queja con Overflow por este obvio 'casting' malo?

public void Overflow() 
{ 
    Int16 s = 32767; 
    s = (Int16) (s + 1); 
} 

En el momento de la compilación, es evidente que (s + 1) no es un Int16 más como sabemos el valor de s.

Y CLR permite la fundición a:

  • Para su propio tipo
  • o cualquiera de las bases tipos (porque es seguro)

Como Int32 no es Int16 y Int16 es no tipo base de Int32.

Pregunta: ¿Por qué el compilador no falla para el casting anterior? ¿Puede por favor explicarlo desde el punto de vista CLR y compilador?

Gracias

+1

Utiliza el molde para * suprimir * el desbordamiento. Si quieres uno, entonces usa Convert.ToInt16() en su lugar. –

Respuesta

12

El tipo de la expresión s + 1 es Int32 - ambos operandos se convierten a Int32 antes se lleva a cabo la adición. Por lo que su código es equivalente a:

public void Overflow() 
{ 
    Int16 s = 32767; 
    s = (Int16) ((Int32) s + (Int32) 1); 
} 

así que el desbordamiento sólo se produce realmente en la conversión explícita.

O, para decirlo de otra manera: porque la especificación del lenguaje lo dice. Debe describir uno de:

  • por qué cree que el compilador está violando la especificación del lenguaje
  • El cambio exacto que está proponiendo a la especificación del lenguaje

EDIT: para hacer las cosas muy claras (sobre la base de sus comentarios), el compilador no permitiría esto:

s = s + 1; 

cuando s es un Int16sea cual sea el valor de, se podría saber que es s. No hay Int16 operator+ (Int16, Int16) operador - tal como se muestra en la sección 7.8.4 de la especificación C# 4, los operadores de suma de enteros son:

int operator +(int x, int y); 
uint operator +(uint x, uint y); 
long operator +(long x, long y); 
ulong operator +(ulong x, ulong y); 
+0

Sí.Y también la especificación del lenguaje no dice que se supone que el compilador debe hacer un análisis de flujo de datos y "saber" que s tendrá un valor constante (excepto si realmente lo declaró como const, que no lo hizo). – usr

+1

Jus como una nota: S + 1 es int32 porque "1" es int32. – TomTom

+0

@usr: si no hace ningún análisis, entonces por qué "s = s + 1;" no compila? – pencilCake

1

"En el momento de la compilación, es evidente que (s + 1) no es un Int16 ya como sabemos el valor de s. "

Nos conocemos el valor de s + 1 es demasiado grande para un corto; el compilador no. El compilador sabe tres cosas:

  1. tiene una variable corta 's'
  2. Se le asignó una constante corta válida.
  3. Realizó una operación integral entre dos valores que tienen conversiones implícitas a int.

Sí, en este caso específico es trivial para determinar que el resultado es demasiado grande para caber en un corto cuando encasillado espalda, pero para determinar que se requeriría que el compilador para realizar operaciones aritméticas en tiempo de compilación , luego realice una verificación de validación de tipo de letra en el resultado. Con muy raras excepciones (todas llamadas explícitamente en la especificación, la mayoría involucran constantes de valor cero) el compilador no verifica los resultados de las operaciones, solo tipos de operaciones, y los tipos de sus operaciones son todos correctos.

Además, su lista de casos en los que el compilador permite el casting es lamentablemente inadecuada. El compilador permite que los tipos de transmisión ocurran en una gran variedad de situaciones, muchas de las cuales son completamente invisibles para el CLR. Por ejemplo, existen conversiones de tipo implícitas y explícitas integradas en el lenguaje para casi cada tipo numérico a cualquier otro tipo numérico. Un buen lugar para encontrar información adicional sobre las reglas typecast:

+0

Es curioso que 'long' sea implícitamente convertible a' float', pero 'double' no lo es, a pesar del hecho de que para cada valor' double' posible, hay exactamente un "mejor" valor 'float', y' long 'puede perder mucha precisión en la conversión. En realidad, postulo que las conversiones de 'float' a' double' son más propensas a tener errores que 'double' a' float'. Por ejemplo, 'float f = (float) (1.0/10.0);' es correcto dentro de la mitad de un bit menos significativo, mientras que 'double d = 1.0f/10.0f;' está desactivado en millones. – supercat

1

En general ese elenco dice "yo estoy haciendo esto deliberadamente, no se quejan", por lo que el compilador se queje sería el comportamiento sorprendente.

De hecho, no hay desbordamiento debido a la promoción implícita de los argumentos. Sin embargo, el elenco trunca el resultado de 32 bits para que el resultado no sea aritméticamente igual a s + 1; pero debido a que solicitó explícitamente el elenco, el compilador no se quejará, está haciendo exactamente lo que usted pidió.

Además, hay muchos casos en los que el desbordamiento "envolvente" (o el módulo 2 n aritmética) es deliberado y obligatorio. El compilador asumirá razonablemente que es necesario si realiza un envío explícito a un tipo más pequeño.

está abajo al programador elegir un tipo apropiado para la operación, y si desbordamiento no es deseable un float, double o decimal puede haber tipos aritméticas más apropiadas que los tipos enteros limitado por el sistema.

+0

sin el molde, el código no se compilará, pero no tiene nada que ver con los rangos y todo lo relacionado con el tipo no coincidente. –

+0

@Michael: ¡Gracias por la aclaración (programador de C++, usuario poco frecuente de C#). Quise decir "solicitó explícitamente el reparto" en lugar de "cambiar el tipo de variable", y no estaba sugiriendo que se omitiera el elenco solo. El punto es si el compilador te dice que hay una discrepancia de tipo y "lo resuelves" mediante conversión a un tipo inapropiado, en lugar de cambiar el tipo de la variable asignada, que no es "detectable" por el compilador porque puede ser un error semántico, o puede ser un requisito de la aplicación. – Clifford

-2

Gracias a todos por sus explicaciones.

Permítanme añadir también una cita del libro de Jeffry Richter que explica por qué compilador no falla cuando se intenta convertir Int16 a Int32 mientras que no se derivan unos de otros:

Desde la página 116:

"(...) el compilador de C# tiene conocimiento íntimo de los tipos primitivos y aplica sus propias reglas especiales al compilar el código . En otras palabras, el compilador reconoce patrones de programación común y produce la IL necesario hacer la código escrito funciona como exp ected ".

+4

Sin ofender a Jeffry Richter, pero esa es una explicación poco convincente. Ya has recibido algunas respuestas mucho mejores. –

Cuestiones relacionadas