Inicio > .NET Framework, C#, Cuestionario > C# 101: control de un programa

C# 101: control de un programa


Este tema, la verdad, es que sobra un poco para todos aquellos que ya conocen C#. Sin embargo, viene dentro de los temas a tratar para la certificación 70-483, así que -nevertheless- lo incluiré, pero a guisa de cuestionario, para que sea más concreto.

 

¿Qué es una expresión?

Una expresión, usualmente ubicada entre dos paréntesis, consiste en la ejecución secuencial de operaciones (las cuales pueden incluir llamadas a métodos de funciones) y operadores que pueden ser evaluadas a un solo valor, objeto, método. Las expresiones pueden tener valores literales, invocación de métodos, o elementos de nombre simple (como variables).

Ejemplo de expresiones:

 

x == 5

10 < x && x <= Convert.ToInt32("256")

(x + Int32.Parse("42")) / 7

 

¿Cómo se evalúa una expresión?

Primero se determina el tipo de dato de la expresión. Si x, y son enteros, entonces x + y será un entero. Si las variables en la expresión son diferentes, se mandan llamar los operadores para los objetos, y si no existen, se realiza una conversión implícita. Si no existe conversión implícita, se lanza un error e compilación.

Si la expresión no incluye operadores, se evalúan los valores de retorno del método invocado. Por ejemplo, new object() evalúa a una nueva referencia.

¿Puede una expresión que involucra variables de un tipo determinado, al evaluarse, generar otro tipo de dato?

Cuando dicha evaluación se realiza mediante métodos, es posible, ya que el resultado depende del tipo de retorno del mismo. Cuando se aplica sobre tipos de datos que tienen operadores sobrecargados, es posible, si éstos así lo determinan.

Cuando se aplica sobre tipos de datos primarios (i.e. estructuras de System), no. Sin embargo, si una expresión da como resultado un valor mayor al que puede almacenar, se lanza un OverflowException. Por ejemplo:

 

int i = int.MaxValue;
int v = i + 1; // OverflowException

 

¿Cómo puede evitarse un OverflowException?

 

Una alternativa es utilizar la palabra reservada unchecked.

int i = int.MaxValue;
int v = 0;

unchecked {
    v = i + 1;
} 

La palabra unchecked hace que si hay un overflow, en lugar de lanzar una excepción, se trunca el valor de la variable. Para hacer lo contrario: forzar un overflow, se utiliza la palabra reservada "checked".

 

¿Qué es una estructura de decisión?

 

Las estructuras de decisión son construcciones de un lenguaje de programación, en este caso C#, que permiten determinar si un bloque de código se ejecuta o no.

Las estructuras de decisión evalúan una o más expresiones. Éstas, en el caso de C#, deben de evaluarse a un valor booleano: verdadero o falso. Si la expresión se evalúa a verdadero, entonces se ejecuta el bloque de código. Algunas estructuras permiten que se ejecute un bloque alterno cuando se evalúa a falso.

 

¿Qué estructuras de decisión existen?

 

Existen tres tipos de estructuras de decisión: las estructuras de implicación, las estructuras de selección y un operador.

Las primeras ejecutan bloques de código de acuerdo a la evaluación de una expresión en particular. Las segundas, deciden qué bloque, de un conjunto amplio, deberán ejecutarse, dependiendo de la coincidencia entre un valor dado y una lista de posibles valores. Finalmente, existe en C# un operador ternario que permite seleccionar un valor dada la evaluación de una expresión booleana.

 

¿Cuáles son las sentencias de implicación?

 

La más común y más utilizada es la sentencia implicativa: si x entonces y.

if (expresión) {
    ...bloque…
}

 

Si expresión es verdadera, se ejecuta el bloque de código. Si el bloque es de una sola sentencia, pueden omitirse las llaves. Si la expresión es falsa, el programa se salta todo el bloque enterito y continúa la ejecución.

Una variante es la sentencia implicativa doble: si x entonces y, y si no, z.

if (expresión) {
    ...bloque 1…
} else {
    ...bloque 2…
}

Si expresión es verdadera, se ejecuta el bloque de código 1. Si no, en automático se ejecuta el bloque 2.

 

Otra variante es la implicación múltiple: si x1 entonces y1, o si no, si x2 entonces y2, o si no, …, o si no, z.

if (expresión 1) {
    ...bloque 1…
} else if (expresión 2) {
    ...bloque 2…
} else if (expresión 3) {
    ...bloque 3…
} else  {
    ...bloque 4…
}

Como puedes ver, podemos combinar la evaluación de diversas expresiones. Si expresión 1 no se cumple, se evalúa expresión 2. Si ésta tampoco se cumple, pasamos a expresión 3. Y así sucesivamente. La última sentencia, el bloque else, es opcional, y si se incorpora entonces se ejecuta el bloque cuya expresión o  haya sido  aprobada por los bloques.

Ejemplo:

Random rnd = new Random();
int n = rnd.Next();

if (n % 2 == 0)
    Console.WriteLine("{0} es un número par", n);
else if (n % 3 == 0)
    Console.WriteLine("{0} es un múltiplo de 3", n);
else if (n % 5 == 0)
    Console.WriteLine("{0} es un múltiplo de 5", n);
else 
    Console.WriteLine("{0} es un número impar", n);

 

 

¿Cuáles son las sentencias de selección?

 

La estructura de decisión de tipo sentencia de selección es la que permite evaluar un valor y luego compararlo con una lista de valores posibles. Esta sentencia se llama switch, y tiene la siguiente estructura:

switch (expresión)
{
    case valor 1:
        ...bloque 1…
        break;

    case valor 2:
        ...bloque 2…
        break;

    …

    case valor n:
        ...bloque n…
        break;

    default:
        ...bloque predefinido…
        break;
}

En esta estructura se evalúa expresión y se compara contra los valores puestos en cada una de las sentencias case. Se compara case a case, y cuando coincide, se ejecuta el bloque de código que existe entre dicho case y la sentencia break. Los break son obligatorios siempre, si no hay error de compilación (nota: esto es diferente a C++, donde si no hay un break se ejecuta código en cascada). Si ningún case concuerda, entonces se ejecuta el bloque de código de la sentencia default.

opcional, pero siempre debe haber por lo menos una sentencia case o una default para que compile.

Por cierto, que los valores a comparar en las sentencias case, DEBEN SER CONSTANTES. Muy importante, no podemos

El bloque default es colocar expresiones ahí, sino solo valores constantes: números, caracteres, o texto, pero no llamadas a funciones ni operadores. Hacerlo generará un error de compilación.

enum Command {
    New, Open, Save, Exit
}

Command cmd = GetCommand();
switch (cmd)
{
    case Command.New:
        SaveCurrentDocument();
        CloseCurrentDocument();
        OpenNewDocument();
        break;

    case Command.Open:
        SaveCurrentDocument();
        CloseCurrentDocument();
        string file = ShowOpenDialog();
        OpenDocument(file);
        break;

    case Command.Save:
        SaveCurrentDocument();
        break;

    case Command.Exit:
        SaveCurrentDocument();
        CloseCurrentDocument();
        Exit();
        break;

    default:
        MessageBox.Show("Comando no reconocido.");
        break;
}

¿Y el operador ternario?

 

El operador ternario ? : permite seleccionar un valor de dos opciones, dependiendo de si la expresión se evalúa a verdadero o falso.

var variable = expresión ? valor 1 : valor 2;

En este caso, expresión debe evaluarse a algún valor booleano. De ser verdadero, se evalúa la expresión de valor 1, si no, se hace lo propio con valor 2.

Algo a notar es que valor 1 y valor 2 deben ser del mismo tipo de dato, y este tipo es el resultado de toda la expresión, y por tanto la variable que recibe debe coincidir con el tipo de dato regresado.

string str = DateTime.Now.Month == 12 ? "Feliz Navidad" : "Es un mes cualquiera.";

try 
{
    …
} 
catch (Exception ex) 
{
    throw new Exception(
        ex is ArgumentException ? "Bug encontrado." : ex.Message
    );
}

El primer ejemplo muestra cómo usar el operador ternario de forma tradicional. En el segundo, el operador se pasa como una expresión que evalúa hacia una cadena de texto, la cual es pasada como parámetro al constructor de Exception.

 

¿Qué es una estructura de iteración?

 

Es un conjunto de sentencias que permiten realizar bucles hasta que cierta condición se cumpla. Estas sentencias pueden tener los siguientes componentes:

1.- Inicialización de variables. Las variables que se utilizan para controlar el bucle se inician a algún valor en particular.

2.- Actualización de valores. En cada iteración, es posible que se actualice el valor de las variables usadas para controlar el bucle.

3.- Evaluación de expresión. En cada bucle, se evalúa una expresión, la cual determina si el bucle debe continuar o no.

4.- Salida forzada. Un bucle puede terminar de forma explícita, mediante la palabra reservada "break".

5.- Continuación forzada. Un bucle puede saltarse a la siguiente iteración, abandonando la actual, mediante la palabra reservada "continue".

Existen cuatro estructuras de iteración: while, do, for y foreach.

 

¿Cuál es la estructura while?

 

Es aquella estructura de iteración que evalúa una expresión, y si ésta es verdadera, ejecuta un bloque de código; cuando éste termina, vuelve a evaluar la expresión, y así sucesivamente. No tiene inicialización de variables ni actualización de valores integrado, así que eso corre por cuenta del programador.

while (expresión)
{
    bloque de código;
}

Si el bloque de código es de una sola línea, pueden omitirse las llaves.

Hay que tener cuidado con la salida del bucle while. Los valores de expresión nunca son actualizados, al menos el bucle no obliga a hacerlo, por lo que si no tenemos cuidado podemos provocar que la estructura quede iterando de forma infinita, trabando nuestro programa o acabándose la memoria. Lo normal es que se inicialice una variable al inicio, que se usa en expresión, y en el bloque de código, ésta se actualiza.

int n = 0;
while (n < 42)
{
    Console.WriteLine("Número {0}", n);
    n++;
}

Este sencillo ejemplo muestra cómo iterar sobre una variable: la iniciamos a cero, y en cada iteración la actualizamos en uno. Aquí la expresión evalúa verdadero mientras el número sea inferior a 42.

string cmd = "M";
while (cmd != "Q")
{
    if (cmd == "M")
        ImprimirMenu();
    else if (cmd == "C")
        CrearReporte();
    else if (cmd == "H")
        ImprimirAyuda();
    else if (cmd == "E")
        EliminarReportes();
    else
        Console.WriteLine("Comando inválido. ");

    cmd = Console.ReadLine();
}

Este ejemplo está un poco más complejo. Se inicializa un comando, y dependiendo de la entrada del usuario, el bucle repite el mismo comportamiento hasta que el usuario da el comando de "Q" para Quitar.

 

¿Cuál es la estructura do?

 

Es una estructura muy similar a la while. De hecho también se le conoce como do-while. Mientras que el while evalúa la expresión y luego ejecuta el bloque, el do primero ejecuta el bloque y luego evalúa la expresión. Tampoco tiene inicialización ni actualización de variables integrado.

do
{
    bloque de código;
} 
while (expresión);

Vamos a modificar el ejemplo anterior para usar un do-while.

string cmd = string.Empty;
do
{
    if (cmd == "M")
        ImprimirMenu();
    else if (cmd == "C")
        CrearReporte();
    else if (cmd == "H")
        ImprimirAyuda();
    else if (cmd == "E")
        EliminarReportes();
    else
        Console.WriteLine("Ingrese un comando válido. ");

    cmd = Console.ReadLine();
} 
while (cmd != "Q");

Con esta estructura retrasamos la actividad de actualizar la variable. Esto puede ser bueno, particularmente cuando dicha actualización consume muchos recursos o es intensiva.

 

¿Cómo elijo entre usar un while o un do?

 

A veces es cuestión de gusto. Pero en general, una buen regla a considerar es la siguiente: si tu bucle puede que nunca ejecute una iteración (es decir, la iteración puede realizarse 0 o más veces), entonces usa un while. Si tu bucle siempre va a ejecutarse una sola vez, entonces una un do.

Esto, porque la evaluación de la expresión en un do se hace al final, forzando ya por lo menos una iterción. En el caso del while se hace al inicio, por lo que si la primera evaluación de la expresión es false, el bucle nunca se ejecutará.

 

¿Cuál es la estructura de iteración for?

 

Es una estructura que tiene tres secciones en su declaración: la inicialización de variables (usualmente utilizada para controlar el bucle), la evaluación de continuidad (i.e. si se evalúa a verdadero, el bucle continúa ejecutándose), y la expresión de actualización de variables (i.e. actualizar las variables de control).

for (inicialización; continuación; actualización)
{
}

Por ejemplo, queremos ejecutar 10 veces un bucle e imprimir un mensaje en pantalla. Inicializamos una variable a 0, la continuación sería que dicha variable sea menor a 10, y la actualización: aumentamos nuestra variable en 1. 

for (int i = 0; i < 10; i++)
{
    Console.WriteLine("Ejecutando {0}… ", i+1);
}

Si no queremos inicializar o actualizar, dejamos vacíos los espacios, pero con el punto y coma:

for (; i < 10;) {
    …
}

Así, podemos recorrer un array muy fácilmente:

string[] strs = GetStringArray();
for (int i = 0; i < strs.Length; i++)
{
    Console.WriteLine(strs[i]);
}

Hay muchas otras formas de utilizar un bucle for, no necesariamente tiene que ser con contadores. Por ejemplo, podemos cambiar el ejemplo anterior por esto:

string[] strs = GetStringArray();
IEnumerable enumerable = strs;
IEnumerator e = enumberable.GetEnumerator();

for (e.Reset(); e.MoveNext(); ) 
{
    string str = e.Current as string;
    Console.WriteLine(str);
}

En este caso, MoveNext regresa false si ya no hay más elementos a evaluar. Por lo que no necesitamos una sentencia de actualzación y la dejamos vacía.

 

¿Cuál es la estructura foreach?

 

La estructura foreach es en realidad azúcar sintáctica para trabajar con enumeraciones. Aunque no es objetivo de esta entrada hablar sobre las enumeraciones, recordaremos que una interfaz IEnumerable (que implementa cualquier colección, array, etc.) tiene un método: GetEnumerator, que nos regresa una interfaz IEnumerator.

Esta última interfaz tiene  dos métodos y una propiedad: Current, que representa el objeto al que apunta el iterador, Reset, que mueve el apuntador del iterador al inicio de la colección, y MoveNext, que avanza en uno el apuntador del iterador. Así, para recorrer una colección, podríamos hacerlo con un bucle for, como vimos en la pregunta pasada:

string[] strs = GetStringArray();
IEnumerable enumerable = strs;
IEnumerator e = enumberable.GetEnumerator();

for (e.Reset(); e.MoveNext(); ) 
{
    string str = e.Current as string;
    Console.WriteLine(str);
}

Sin embargo, esto  es un poco complejo, y C# pone a nuestra disposición los bucles foreach. Estos bucles se convierten en bucles for como el anterior, pero ocultan la complejidad. Por tanto, el bucle foreach sólo funcionará con colecciones que implementen IEnumerable. Básicamente declaramos una variable (equivalente al Current) y declaramos sobre qué colección se iterará. Internamente, el bucle foreach se hará cargo de llamar a Reset y MoveNext para saber dónde acaba la colección. 

foreach (Tipo variable in colección)
{
}

El ejemplo anterior podríamos dejarlo como:

string[] strs = GetStringArray();

foreach (string str in strs)
{
    Console.WriteLine(str);
}

Mucho más fácil, ¿no?

Por supuesto, el foreach se adhiere a las reglas de IEnumerator. En particular, mientras se está iterando de esta forma, la colección no puede ser modificada. Si se modifica, tendremos una bonita excepción InvalidOperationException en nuestras manos.

List<string> strs = GetStringList();

foreach (string str in strs)
{
    strs.Remove(str); // InvalidOperationException
}

 

Palabras finales

 

Bueno, hemos visto las estructuras de decisión e iteración. Fáciles de utilizar, se emplean en todo programa por todos lados. Esperemos que esto ayude en los esfuerzos para certificarnos en 70-483.

Categorías:.NET Framework, C#, Cuestionario Etiquetas: , ,
  1. Aún no hay comentarios.
  1. No trackbacks yet.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s