multiple - switch case c# windows forms
¿Qué está pensando el compilador acerca de la instrucción switch? (3)
Bien, entonces el problema con esto es que el compilador optimiza completamente el interruptor, y aquí hay una prueba:
static void withoutVar()
{
Console.WriteLine("Before!");
switch (1)
{
case 2:
}
Console.WriteLine("After!");
}
El cual, cuando se descompila con ILSpy, nos muestra esta IL:
.method private hidebysig static
void withoutVar () cil managed
{
// Method begins at RVA 0x2053
// Code size 26 (0x1a)
.maxstack 8
IL_0000: nop
IL_0001: ldstr "Before!"
IL_0006: call void [mscorlib]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: br.s IL_000e
IL_000e: ldstr "After!"
IL_0013: call void [mscorlib]System.Console::WriteLine(string)
IL_0018: nop
IL_0019: ret
} // end of method Program::withoutVar
Que no tiene ningún recuerdo de una declaración de cambio en cualquier lugar. Creo que la razón por la que no optimiza el segundo también puede tener algo que ver con la sobrecarga del operador y el tipo. Entonces, podría ser posible que tenga un tipo personalizado que cuando se asigna a 1
, se convierta en 2
. Sin embargo, no estoy del todo seguro, me parece que se debe enviar un informe de error.
¡Inspirado de una pregunta -5
otra vez!
¡Leí [ este comentario ] de @Quartermeister y me @Quartermeister asombrado!
Entonces por qué esto compila
switch(1) {
case 2:
}
pero esto no lo hace.
int i;
switch(i=1) {
case 2: // Control cannot fall through from one case label (''case 2:'') to another
}
ni esto
switch(2) {
case 2: // Control cannot fall through from one case label (''case 2:'') to another
}
actualizar:
La pregunta -5
convirtió en -3
.
En Visual Studio 2012, la razón de la primera es obvia. El compilador determina que el código es inalcanzable:
switch (1)
{
case 2:
}
Advertencia: Se ha detectado un código inalcanzable.
En los otros dos casos, el compilador informa "El control no puede pasar de una etiqueta de caso (''caso 2:'') a otra". No veo que diga "(''caso 1'')" en ninguno de los casos fallidos.
Supongo que el compilador simplemente no es agresivo acerca de la evaluación constante. Por ejemplo, los siguientes son equivalentes:
int i;
switch(i=1)
{
case 2:
}
y
int i = 1;
switch(i)
{
case 2:
}
En ambos casos, el compilador intenta generar código, cuando podría hacer la evaluación y determinar que lo que está escribiendo es:
switch (1)
{
case 2:
}
Y determina que el código es inalcanzable.
Sospecho que la respuesta será "¿por qué no compila esta"? "Porque permitimos que el compilador JIT maneje una optimización agresiva".
Ninguno de ellos debería compilar. La especificación de C # requiere que una sección de switch tenga al menos una declaración. El analizador debe rechazarlo.
Ignoremos el hecho de que el analizador permite una lista de instrucciones vacía; Eso no es lo relevante. La especificación dice que el final de la sección del interruptor no debe tener un punto final alcanzable; ese es el bit relevante
En su último ejemplo, la sección del interruptor tiene un punto final alcanzable:
void M(int x) { switch(2) { case 2: ; } }
por lo que debe ser un error.
Si tuvieras:
void M(int x) { switch(x) { case 2: ; } }
entonces el compilador no sabe si x alguna vez será 2. Asume de forma conservadora que podría, y dice que la sección tiene un punto final alcanzable, porque la etiqueta de la caja del interruptor es accesible.
Si tuvieras
void M(int x) { switch(1) { case 2: ; } }
Entonces el compilador puede razonar que el punto final no es alcanzable porque la etiqueta del caso no es accesible. El compilador sabe que la constante 1 nunca es igual a la constante 2.
Si tuvieras:
void M(int x) { switch(x = 1) { case 2: ; } }
o
void M(int x) { x = 1; switch(x) { case 2: ; } }
Entonces sabes y sé que no se puede alcanzar el punto final, pero el compilador no lo sabe. La regla en la especificación es que la accesibilidad solo se determina analizando expresiones constantes. Cualquier expresión que contenga una variable, incluso si conoce su valor por algún otro medio, no es una expresión constante.
En el pasado, el compilador de C # tenía errores donde este no era el caso. Se podrían decir cosas como:
void M(int x) { switch(x * 0) { case 2: ; } }
y el compilador razonaría que x * 0 tenía que ser 0, por lo tanto, la etiqueta del caso no es accesible. Eso fue un error, que solucioné en C # 3.0. La especificación dice que solo se usan constantes para ese análisis, y que x
es una variable, no una constante.
Ahora, si el programa es legal , el compilador puede usar técnicas avanzadas como esta para influir en qué código se genera. Si dices algo como:
void M(int x) { if (x * 0 == 0) Y(); }
Entonces el compilador puede generar el código como si hubieras escrito
void M(int x) { Y(); }
si quiere Pero no puede usar el hecho de que x * 0 == 0
es verdadero para determinar la accesibilidad de la declaración.
Por último, si tienes
void M(int x) { if (false) switch(x) { case 2: ; } }
entonces sabemos que el interruptor no es alcanzable, por lo tanto, el bloque no tiene un punto final alcanzable, por lo que esto es, sorprendentemente, legal. Pero dada la discusión anterior, ahora sabes que
void M(int x) { if (x * 0 != 0) switch(x) { case 2: ; } }
no trata x * 0 != 0
como false
, por lo que el punto final se considera alcanzable.