domingo, 31 de octubre de 2010

Uso de unchecked en C#

En este post mencione sobre algunos ejemplos que tome del libroJava Puzzlers: Traps, Pitfalls, and Corner Cases de Joshua Bloch y Neal Gafter publicado por Addison Wesley Professional en el año 2005 donde vienen curiosidades y trucos acerca de la programación con Java, Los siguientes ejemplos se encuentran en el libro y requieren el uso en C# de los operadores checked y unchecked para controlar la comprobación de overflow en operaciones aritméticas y conversiones.

Siempre que trabajemos con operaciones numéricas o con conversiones en nuestros programas hay posibilidades de que suceda un overflow (desbordamiento) cuando el resultado de dicha operación sobrepase la capacidad mínima o máxima de la variable que usemos para contener ese resultado. Los siguientes listados ejemplifican el uso de estos operadores.

Fig 1. El primer ejemplo sin la palabra unchecked.


Fig 2. El segundo ejemplo sin la palabra unchecked.



En el primer caso el compilador envía el siguiente error:
Puzzle4.cs(12,50): error CS0220: The operation overflows at compile time in checked mode


Para el segundo caso el mensaje es similar

Puzzle6.cs(10,30): error CS0221: Constant value `-1' cannot be converted to a `byte' (use `unchecked' syntax to override) Compilation failed: 1 error(s), 0 warnings



Debido a que en los dos listados el overflow puede ser comprobado en tiempo de compilación, en C# de forma predeterminada todo el código asignado a constantes es comprobado aunque este dentro de un bloque unchecked, y para las variables si el overflow ocurre en tiempo de ejecución el programa lanza una OverflowException excepto que sea suprimida usando unchecked.

Por lo tanto para compilar los listados, debemos de usar un bloque unchecked en la asignación de las siguientes variables en el primer listado:


unchecked{
const long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000;
const long MILIS_PER_DAY = 24 * 60 * 60 * 1000;
resp = (MICROS_PER_DAY / MILIS_PER_DAY);
}

Y un bloque similar en el segundo listado.


unchecked
{
Console.WriteLine((int)(char)(byte)-1);
}

El uso de unchecked en el primer listado ocasiona que no se produzca una excepción aunque ocurra el overflow esto siempre ocasiona que los valores sean truncados, por lo que el resultado de la ejecucción de este programa no es lo esperado.



Para solucionar por completo el código del listado 1 debemos agregar el caracter L al final del primer término (en este ejemplo el número 24), así toda las subsecuentes expresiones seran de tipo long.
El bloque de código se muestra a continuación.



const long MICROS_PER_DAY = 24L * 60 * 60 * 1000 * 1000;
const long MILIS_PER_DAY = 24L * 60 * 60 * 1000;
resp = (MICROS_PER_DAY / MILIS_PER_DAY);

El código final del ejemplo 1 utilizando la palabra unchecked


El código final del ejemplo 2 utilizando la palabra unchecked



La salida al ejecutar el listado 1 es


y la salida al ejecutar el listado 2 es


  Descarga el código fuente

lunes, 25 de octubre de 2010

Expresiones Lambda (Lambda Expressions) con C#

Las expresiones lambda provienen del cálculo lambda (lambda calculus) desarrollado por Alonzo Church en los años 1930’s como una notación para representar todas las funciones computables equivalentes a una máquina de Turing, todos los lenguajes funcionales pueden ser vistos como una variante sintáctica del cálculo lambda.
Las expresiones Lambda son útiles para sintetizar funciones con pocos parámetros que regresan algún valor, esta expresión consiste básicamente en una regla de sustitución que expresa tal cual una función o sea un mapeo de los elementos del conjunto dominio a los elementos de un codominio por ejemplo en la siguiente expresión:



cuadrado : integer → integer donde cuadrado(n) = n²

Aunque C# no utiliza los símbolos de la notación matemática lambda, el operador lambda es => que significa “tiende a” o “va hacia a”, la estructura de una expresión lambda en C# es:



(Argumentos de entrada) => (salida al procesarlos)

Veamos algunos ejemplos:



La salida del programa es la siguiente imagen


Ahora un ejemplo con funciones estadísticas:


la salida de este ejemplo es la siguiente imagen


Es importante saber que es el tipo delegate que dicta el tipo de los parámetros de entrada y de salida.


  Descarga el código fuente

domingo, 3 de octubre de 2010

Conversiones y curiosidades acerca de C#

En el libro Java Puzzlers: Traps, Pitfalls, and Corner Cases de Joshua Bloch y Neal Gafter publicado por Addison Wesley Professional en el año 2005 encontré ciertas curiosidades y trucos acerca de la programación en Java que me llamaron la atención, los autores explican la razón de tales escollos por lo que tome algunos ejemplos del libro para programarlos con C# y ver si obtendría los mismos resultados que sus contrapartes en Java.

Aquí algunos ejemplos del libro con las mismas preguntas, la diferencia únicamente es la implementación de Java a C#.

1-. En el siguiente programa ¿Es en todos los casos seguro que el método IsOdd determine correctamente si el número entero es par o no?


2-.¿Qué valor imprime el siguiente programa?


3-. ¿Cuál es el resultado del siguiente programa?


Analicemos cada uno de los ejemplos.

Ejemplo 1

Observaciones Al ejecutar el programa y pasarle un número entero como argumento desde la línea de comandos al parecer el programa determina si ese número es par o no, como en las siguientes imagen.


Pero el método que determina si el número es par o impar falla al pasarle un número negativo par o impar, esto siendo que toma como parámetro un entero y la mitad de los valores del tipo entero son negativos, el método supone que los residuos son únicamente positivos siendo que cuando la operación del residuo regresa un número que no es cero conserva el signo del dividendo.

La solución entonces es cambiar el método para que la comparación en vez de quedar igual a uno cambie a diferente a cero


static bool IsOdd(int i)
{
return i % 2 != 0;
}

La siguiente imagen nos muestra el resultado:

Ejemplo 2


Observaciones Este ejemplo se refiere a una mala práctica de programación, si examinamos el código puede que no nos percatemos que el último digito del segundo sumando en lugar de ser el número uno (1) es la letra l (ele) minúscula, por lo que una de las buenas prácticas de programación señaladas en el libro es no utilizar la letra l (ele) minúscula para indicar un valor numérico de tipo long o como variable.



Console.WriteLine(12345 + 5432l);

En todo caso el compilador de C# nos lanza la advertencia al compilar el programa,


Puzzle2.cs(9,34): warning CS0078: The 'l' suffix is easily
confused with the digit '1' (use 'L' for clarity)
Compilation succeeded - 1 warning(s)

que nos recomienda usar siempre la letra mayúscula para evitar confusiones, como se muestra en la siguiente imagen.



Ejemplo 3

Observaciones si examinamos el código del programa, al ejecutarlo debería de imprimir XX o sea dos veces el carácter ‘X’, un carácter por cada expresión por expresión, sin embargo al ejecutar el programa imprime 8888 como en la siguiente imagen:



Esto se debe a ciertas reglas del operador ternario u operador condicional (condicional operator) donde el primer operando debe ser de tipo bool que se cumple en el caso de ambas expresiones, la diferencia está en el segundo y tercer operador donde las reglas son:

  1. Si los dos operadores son del mismo tipo la expresión devuelve ese tipo.
  2. Si hay una conversión implícita del segundo al tercero pero no del tercero al segundo entonces la expresión devuelve el tipo del tercer operador.
  3. Si hay una conversión implícita del tercero al segundo pero no del segundo al tercero entonces la expresión devuelve el tipo del segundo operador.

En el ejemplo 3 en la primera expresión existe la conversión implícita de char a int por lo que valor de la expresión es de tipo int y a imprimir es de tipo carácter se cumple la primera regla y en la segunda expresión no existe conversión implícita de int a char por lo se cumple la tercera regla.

En ambos casos es el equivalente de

Console.Write((int)x);
Para eliminar la confusión cambiemos el código a una conversión explicita:


El resultado es el esperado como se muestra en la siguiente imagen:


  Descarga el código fuente