Operadores Checked y Unchecked

jueves, 1 de febrero de 2007

Existen en C# gran cantidad de tipos predefinidos numéricos sbyte, byte, short, ushort, int, uint, long, ulong, etc.

Cuando uno mira código en general no es tan común ver estos tipos de datos, la mayoría de nosotros definimos nuestras variables del tipo int.

Esto tiene sus implicancias por un lado el tipo int abarca valores desde -2,147,483,648 hasta 2,147,483,647 (32 bits) esto en general nos mantiene cubiertos para contadores comunes, pero estamos reservando mucho espacio de memoria inútilmente, si por ejemplo queremos contar quizás hasta 100.

Ahora bien siendo conscientes de esto, uno podría optar por pensar un poco mas, que tipo de dato utilizar para definir nuestras variables, pero ¿sabemos que es lo que pasa si nos pasamos del rango que cada tipo de dato tiene como limite?, uno tendería a pensar que nos daría un error, pero atención no es así!

Existen dos operadores que habitualmente no utilizamos que nos permiten verificar este tipo de casos, estos son checked y unchecked.

El operador checked, permite chequear si tenemos overflow cuando realizamos operaciones como (++ -- -(unary) + - * / ) o alguna conversión explícita entre tipos, ejemplo pasar de int a byte.

El unchecked hace lo contrario, es decir ignora el overflow y trunca el valor.

Vamos a mostrar un ejemplo:

byte val = 250;
for (int i = 1; i < 15; i++)
{
val++;
}
El tipo de dato byte soporta un rango de valores entre 0 y 255, en esta caso en particular al terminar este ciclo la variable val queda con valor 8 y no da ningún error, lo que esta pasando es que se pasa de su limite, llega al máximo valor soportado por este tipo de dato 255, y vuelve a empezar de cero, si esta variable fuera del tipo int y nos pasáramos de su limite, volvería a empezar desde -2,147,483,648 ya que es un tipo de dato con signo.

Esto claro es un problema si no lo tenemos en cuenta, porque podría darnos resultados inesperados.

Para que esto no ocurra tenemos tres opciones, una es incluir la opción /checked cuando compilamos el proyecto, en cuyo caso se realizaría este tipo de chequeo en toda nuestra aplicación, otra es encerrar este bloque de código con el operador checked y la última es "asegurarnos" que nuestra variable no de overflow.

Vamos a ver el mismo ejemplo pero con chequeo habilitado:
byte val = 250;
checked
{
for (int i = 1; i < 15; i++)
{
val++;
}
}
En este caso el codigo lanza una Exception, del tipo OverflowException.

Es importante tener en cuenta que este tipo de Overflows se puede dar tambien en la conversion explicita de un tipo a otro ejemplo:
int intVal = 256;
byte val = (byte)intVal;
En este caso en particular la variable val queda con valor 0.

El operador unchecked es útil si necesitamos dentro de un bloque checked, dejar parte del código sin chequear.

Conclusión:

La verdad no creo que estos operadores sean muy utilizados, pero lo que creo que es importante tener en cuenta es que si nos excedemos en el rango permitido por una variable numérica, por defecto no se lanza una Exception, que es lo que quizás uno se hubiese imaginado, y por otro lado creo que vale la pena tomarnos un minuto para elegir mejor que tipo de dato vamos a utilizar en cada caso para optimizar nuestras aplicaciones.