sábado, 1 de julio de 2017

Curso de C#: Control de flujo, estructuras condicionales


Instrucción IF...ELSE IF...ELSE


Si esta condición se evalúa a true (verdadero) se ejecutan las líneas del bloque, y si se evalúa a false (falso) no se ejecutan. en C#:

if (num==10)
{
    Console.WriteLine("El número es igual a 10");
}

En este pequeño ejemplo, se evalúa como verdadero o falso lo que está dentro de los paréntesis, es decir, num==10. Por lo tanto, el operador == retornará true siempre que num valga 10, y false si vale otra cosa. No confundir el operador de comparación == con el de asignación =. 

La instrucción if ejecuta el código de su bloque siempre que la expresión que se evalúa retorne true. Sin embargo, no es necesario abrir el bloque en el caso de que solamente haya que ejecutar una sentencia. Así, podríamos haber escrito el ejemplo de esta otra forma:

if (num==10)
    Console.WriteLine("El número es igual a 10");

O bien:

if (num==10) Console.WriteLine("El número es igual a 10");

Curso de C#: Control de flujo, estructuras condicionales


En cualquiera de los dos casos, funciona igual porque, recordemos, el compilador entiende que todo es la misma instrucción mientras no encuentre un punto y coma o una llave de apertura de bloque.

También puede ocurrir que sea necesario ejecutar una serie de acciones si se da una condición y otras acciones en caso de que esa condición no se dé, Para eso existe la instrucción else, en C#:

if (num==10)
{
    Console.WriteLine("El número es igual a 10");
}
else
{
    Console.WriteLine("El número no es igual a 10");
}

Si el bloque consta de una única línea, podemos eliminar las llaves:

if (num==10)
    Console.WriteLine("El número es igual a 10");
else
    Console.WriteLine("El número no es igual a 10");

O bien:

if (num==10) Console.WriteLine("El número es igual a 10");
else Console.WriteLine("El número no es igual a 10");

Si no se ponen las llaves, tanto if como else afectan únicamente a la primera línea que se encuentre tras la condición. Ejemplos:

if (num==10)
    Console.WriteLine("El número es igual a 10");
    Console.WriteLine("Otra linea");
else // Incorrecto: el compilador no sabe a qué if se refiere
    Console.WriteLine("El número no es igual a 10");

------------------------------------------------------

if (num==10)
    Console.WriteLine("El número es igual a 10");
else
    Console.WriteLine("El número no es igual a 10");
    Console.WriteLine("Línea final"); // Esta línea se ejecuta siempre

En el primer caso se produciría un error en tiempo de compilación, porque el compilador no sabrá enlazar el else con el if ya que, al no haber llaves de bloque, da por terminada la influencia de este después de la primera línea que está tras él. En el segundo caso no se produciría un error, pero la última línea (la que escribe "Línea final" en la consola) se ejecutaría siempre, independientemente de si se cumple o no la condición, pues no hay llaves dentro del bloque else, por lo cual este afecta solamente a la línea que le sigue. Para que else afectara a estas dos líneas habría que haber escrito las llaves de bloque:

if (num==10)
    Console.WriteLine("El número es igual a 10");
else
{
    Console.WriteLine("El número no es igual a 10");
    Console.WriteLine("Línea final"); 
}

Ahora, en caso de cumplirse la condición se ejecutaría la línea que hay detrás del if, y en caso contrario se ejecutarían las dos líneas escritas dentro del bloque else.

También es posible que sea necesario enlazar varios if con varios else. En C#:

if (num==10)
{
    Console.WriteLine("El número es igual a 10");
}
else if (num>5)
{
    Console.WriteLine("El número es mayor que 5");
}
else if (num>15)
{
    Console.WriteLine("El número es mayor que 15");
}
else
{
    Console.WriteLine("El número no es 10 ni mayor que 5");
}

Si num vale 10, se ejecutará el bloque del primer if, por lo que la salida en la consola será "El número es igual a 10". Sin embargo, a pesar de que 10 es también mayor que cinco, no se ejecutará el bloque del primer else if, pues ni siquiera se llega a comprobar dado que ya se ha cumplido una condición en la estructura. Si num vale un número mayor que 5, menor que 15 y distinto de 10, o sea, 9, por ejemplo, se ejecuta el bloque del primer else if saliendo "el número es mayor que 5" en la consola. ¿qué sucede si el número es mayor que 15? Pues sucede exactamente lo mismo, ya que, si es mayor que 15 también es mayor que 5, de modo que se ejecuta el bloque del primer else if y después se dejan de comprobar el resto de las condiciones. Por lo tanto, en este ejemplo, el bloque del segundo else if no se ejecutaría en ningún caso. Por último, el bloque del else se ejecutará siempre que num valga 5 o menos de 5, pues es el único caso en el que no se cumple ninguna de las condiciones anteriores.

Las condiciones que se evalúan en un if o en un else if no tienen por qué ser tan sencillas. En C# estas expresiones (las condiciones) han de retornar true o false necesariamente, por lo que es posible utilizar y combinar todo lo que pueda retornar true o false, como variables de tipo bool, métodos o propiedades que retornen un tipo bool, condiciones simples o compuestas mediante los operadores lógicos && (AND lógico) || (OR lógico) y ! (NOT lógico), o incluso mezclar unas con otras. Por ejemplo:

if ((Ficheros.Existe(Archivo) || Crear) && EspacioDisco>1000 )
{
    Console.WriteLine("Los datos se guardarán en el archivo");
}
else
{
    Console.WriteLine("El archivo no existe y no se puede crear o bien no hay espacio");
}

En este ejemplo, "Existe" es un método de la clase Ficheros que retorna true o false, "Crear" es una variable bool y "EspacioDisco" es una variable de tipo uint. 
En una sola condición están combinados varios elementos que pueden retornar valores booleanos. Se encierra entre paréntesis la expresión Ficheros.Existe(Archivo) || Crear porque se necesita que se evalúe todo esto primero, para después comparar con la otra expresión, ya que el operador && se ejecuta antes que el ||. Así esta expresión retornará true en caso de que el método Existe devuelva true, la variable crear valga true o sucedan ambas cosas. Posteriormente se establece el resultado de la otra expresión, es decir EspacioDisco>1000, y después se comparan los dos resultados con el operador &&, obteniendo el resultado final, que será true si ambos operandos valen true, y false si alguno de ellos o los dos valen false. Así, si el archivo existe o bien si se quiere crear en caso de que no exista se guardarán los datos si, además, hay espacio suficiente, y si, por el contrario, el archivo no existe y no se quiere crear o bien si no hay espacio, los datos no se guardarán.

Cuando sea necesario hacer una simple asignación a una variable dependiendo de un determinado valor, podemos hacerlo con if o bien es posible utilizar el operador Question (?:). Por ejemplo,  si tenemos un método en el que necesitamos saber, de entre dos números, cuál es el mayor y cuál el menor. Podemos hacerlo con if, así:

if (num1>num2)
{
    mayor=num1;
    menor=num2;
}
else
{
    mayor=num2;
    menor=num1
}

Pero también es posible hacer la lectura del código algo más cómoda si utilizamos el operador Question, así:

mayor=(num1>num2) ? num1: num2;
menor=(num1>num2) ? num2: num1;
  

Instrucción Switch


Una instrucción switch funciona de un modo muy similar a una construcción con if...else if... else. Sin embargo, hay un diferencia fundamental: mientras en las construcciones if...else if... else las condiciones pueden ser distintas en cada uno de los if ... else if, en un switch se evalúa siempre la misma expresión, comprobando todos los posibles resultados que esta pueda retornar.
Un switch equivale a comprobar las diferentes situaciones que se pueden dar con respecto a una misma cosa. En C#:

switch (opcion)
{
    case 1:
        descuento=10;
        break;
    case 2:
        descuento=5;
        break;
    case 3:
        descuento=2;
        break;
    default:
        descuento=0;
        break;
}

Solamente se establece un bloque para la instrucción "switch", pero ninguno de los "case" abre ningún bloque (tampoco lo hace "default"). Una vez que se terminan las instrucciones para cada caso hay que poner "break" para que el compilador salga del switch. Si no se cierra el "case" con un "break", pueden ocurrir dos cosas: si queremos que el programa haga las mismas cosas en distintos casos, habrá que poner todos estos casos y no cerrarlos con break. Por ejemplo, si el descuento es 5 tanto para la segunda como para la tercera opción, habría que hacerlo así:

switch (opcion)
{
    case 1:
        descuento=10;
        break;
    case 2:
    case 3:
        descuento=5;
        break;
    default:
        descuento=0;
        break;
}

De este modo, en caso de que opcion valiera 2 ó 3, el descuento sería del 5%, pues, si opcion vale 2, el flujo del programa entraría por case 2 y continuaría por case 3 ejecutando el código de este último al no haber cerrado el case 2 con break, y si opción vale 3 entraría por case 3 ejecutando, por lo tanto, el mismo código. Sin embargo,  no hubiera sido válido establecer acciones para el case 2 sin cerrarlo con break. 

switch (opcion)
{
    case 1:
        descuento=10;
        break;
    case 2:
        regalo="Cargador de CD"
    case 3:
        descuento=5;
        break;
    default:
        descuento=0;
        break;
}

Si la opción vale 2, el flujo entraría por case 2, estableciendo el regalo y seguiría por case 3 estableciendo también el descuento, dado que case 2 no ha sido cerrado con un break. Si opción vale 3 entraría solamente por case 3, estableciendo únicamente el descuento y no el regalo. Esto es muy cómodo si se desea definir acciones específicas para un valor determinado y añadirles otras que comunes para varios valores. Sin embargo, esto no se puede hacer en C#, dado que el compilador avisaría de un error, diciendo que hay que cerrar case 2. Pues, a pesar de la comodidad de esta construcción, para determinadas circunstancias, lo más común es que se omita el break por error que por intención, lo cual provoca muchos fallos que serían muy difíciles de detectar. Por este motivo, los diseñadores del lenguaje C# decidieron que el riesgo no merecía la pena, ya que esta funcionalidad se puede conseguir fácilmente con un simple if, así:

switch (opcion)
{
    case 1:
        descuento=10;
        break;
    case 2:
    case 3:
        if (opcion==2) regalo="Cargador de CD";
        descuento=5;
        break;
    default:
        descuento=0;
        break;
}

La expresión que se ha de comprobar en un switch ha de ser, necesariamente, compatible con los tipos sbyte, byte, short, ushort, int, uint, long, ulong, char o string. Compatible quiere decir que sea de uno de esos tipos o que se pueda convertir a uno de ellos.

No hay comentarios:

Publicar un comentario