Try, Catch
Try, Catch
Recurrimos a la televisin para apagar el cerebro, y a la computadora para encenderlo. Aquel que nunca perdi un archivo, que tire el primer diskette.
6.1
Control de Excepciones
Las excepciones son el mecanismo recomendado en la plataforma .NET para propagar los que se produzcan durante la ejecucin de las aplicaciones (divisiones entre cero, lectura de archivos por ejemplo al tratar de abrir un archivo que no se encuentra, tratar de acceder al miembro de un arreglo cuyo ndice no existe, desbordamiento por nmeros demasiados grandes, al ingresar un carcter cuando se espera un valor numrico, etc.) Bsicamente, son objetos derivados de la clase System.Exception que se generan cuando en tiempo de ejecucin se produce algn error y que contienen informacin sobre el mismo. Todo esto se controla con las palabras clave try, throw, catch y finally . C# proporciona una solucin estructurada tanto a nivel del sistema como de aplicacin. Anteriormente, la informacin de errores consista simplemente en hacer que los mtodos en cuya ejecucin pudiesen producirse devolvieran cdigos que informasen sobre si se han ejecutado correctamente o, en caso contrario, sobre cul fue el error producido. Sin embargo, ahora las excepciones proporcionan las siguientes ventajas: Utilizando excepciones es posible escribir el cdigo como si nunca se fuesen a producir errores y dejar en una zona aparte todo el cdigo de tratamiento de errores, lo que contribuye a facilitar la legibilidad del cdigo fuente. Una excepcin es un objeto que cuenta con campos que describen las causas del error y a cuyo tipo suele drsele un nombre que resuma claramente su causa. Por ejemplo, para informar errores de divisin por cero se suele utilizar una excepcin predefinida de tipo DivideByZeroException en cuyo campo Message se detallan las causas del error producido. Cuando se usan excepciones siempre se asegura que el programador trate toda excepcin que pueda producirse o que, si no lo hace, se aborte la ejecucin de la aplicacin mostrndose un mensaje indicando dnde se ha producido el error. En la plataforma .NET el CLR quien se encarga de detectar y tratar las excepciones y es su recolector de basura quien se encarga asegurar la correcta destruccin de los objetos. Obviamente el cdigo seguir siendo algo ms lento, pero es un pequeo sacrificio que merece la pena hacer en tanto que ello asegura que nunca se producirn problemas difciles de detectar derivados de errores ignorados.
Excepciones ms comunes
En el espacio de nombres System de la BCL hay predefinidas mltiples excepciones derivadas de System.Exception que se corresponden con los errores ms comunes que pueden surgir durante la ejecucin de una aplicacin. En la siguiente tablas se listan algunas:
6.2
Se produce cuando el formato de un argumento no cumple las especificaciones de los parmetros del mtodo invocado. DivideByZeroException Cuando se divide entre cero IndexOutOfRangeException Se genera cuando se intenta tener acceso a un elemento de un arreglo con un ndice que est fuera de los lmites del arreglo. OverflowException Desbordamiento dentro de contexto donde se ha de comprobar los desbordamientos (expresin constante, instruccin checked, operacin checked u opcin del compilador /checked) ArgumentException Pasado argumento no vlido (base de excepciones de argumentos) ArgumentNullException Pasado argumento nulo ArgumentOutOfRangeException Pasado argumento fuera de rango ArrayTypeMistmatchException Asignacin a tabla de elemento que no es de su tipo COMException Excepcin de objeto COM InvalidCastException Conversin explcita entre tipos no vlida InvalidOperationException Operacin invlida en estado actual del objeto InteropException Base de excepciones producidas en comunicacin con cdigo inseguro NullReferenceException Acceso a miembro de objeto que vale null OutOfMemoryException Falta de memoria para crear un objeto con new SEHException Excepcin SHE del API Win32 StackOverflowException Desbordamiento de la pila, generalmente debido a un excesivo nmero de llamadas recurrentes. TypeInizializationException Ha ocurrido alguna excepcin al inicializar los campos estticos o el constructor esttico de un tipo. En InnerException se indica cul es.
try-catch-finally
Un uso comn de catch y finally consiste en obtener y utilizar recursos en un bloque try, tratar circunstancias excepcionales en el bloque catch y liberar los recursos en el bloque finally. Utilizamos la instruccin try para que nuestro programa intente hacer algo. Si esto no es posible, pasara a la orden Catch, sin lanzar ningn error. Una vez ejecutado con xito una orden try, o catch en caso de no poder ejecutar la orden try, pasara a la orden Finally. Lo normal es que haya 1 try, 1 catch, y 1 finally, se recomienda:
6.3
1* Mximo una instruccin try por cdigo (por instruccin). 2* Mximo una instruccin finally por cdigo (por instruccin). 3* Siempre hay que poner, 1 try, y una de las otras dos, cualquiera, pero una.
La estructura general del cdigo try-catch-finally es : try { has esto... si i = 0 lanza una excepcin } catch { si fallo has esto... } finally { haya fallado o no, has esto... } El manejo de excepciones es relativamente sencillo y fcil de entender aunque no tengamos mucha experiencia programando, todo aquello que se puso entre las llaves { } del try es un segmento de cdigo en el que puede o no generarse un error en tiempo de ejecucin(excepcin), en caso de que haya habido un funcionamiento anmalo en el programa(excepcin) la ejecucin del cdigo entra en el segmento de cdigo catch y ejecuta el bloque de instrucciones que hemos definido para manejar ese error, finalmente el flujo del programa haya o no habido excepcin entra en finally aqui podemos poner rutinas para marcar los objetos que ya no se utilizarn de manera que el recolector de basura pueda liberar la memoria que dichos objetos ocupaban, rutinas que guarden un log de la aplicacin para llevar un control de cuntas veces ha fallado?, porqu fallo?, etc., todo bloque try puede tener uno o ms catch para tratar cada una de las posibles excepciones, pero la flexibilidad de C# va ms all de eso, ya que nos permite lanzar nuestras propias excepciones, por ejemplo si un mtodo no recibe un valor que debe recibir o recibe un valor que no puede procesar podemos lanzar nuestra propia excepcin.
Ejemplo de Excepciones
Ahora veamos un ejemplo de excepciones cuando se trata de accesar a un ndice de arreglo que no existe:
6.4
Excepciones1.cs
using System; class Excepciones3 { static void Main(string[] args) { int[] array1={1,2}; int y; try { y=array1[2]; } catch (IndexOutOfRangeException) { Console.WriteLine("Error: IndexOutOfRangeException"); } finally { Console.WriteLine("Esta sentencia siempre se ejecuta"); Console.ReadLine(); } } }
En el siguiente ejemplo de excepciones se utiliza la palabra clave throw, para informar de un error no basta con crear un objeto del tipo de excepcin apropiado, sino que tambin hay pasrselo al mecanismo de propagacin de excepciones del CLR. A esto se le llama lanzar la excepcin, y para hacerlo se usa la siguiente instruccin: throw <objetoExcepcinALanzar> ; Por ejemplo, para lanzar una excepcin de tipo DivideByZeroException se podra hacer: throw new DivideByZeroException(); Si el objeto a lanzar vale null, entonces se producir una NullReferenceException que ser lanzada en vez de la excepcin indicada en la instruccin throw.
6.5
Excepciones2.cs
using System; class MyClass { public static void Main() { MyClass x = new MyClass(); try { string s = null; x.MyFn(s); } catch (Exception e) { Console.WriteLine("{0} Error", e); Console.ReadLine(); } } public void MyFn(string s) { if (s == null) throw (new System.ArgumentNullException()); // Lanzamos la excepcin que indica un argumento nulo. } }
6.6
Ahora veamos un ejemplo de excepciones utilizando la parte grafica, diseemos una forma con los siguientes controles:
Utilizaramos las instrucciones try y catch, el proyecto lo que realiza es la divisin de dos nmeros y muestra el resultado, lo que deseamos es validar las dos entradas ya que si en lugar de un valor numrico escribiramos otro valor por ejemplo un carcter generara el siguiente error:
6.7
En la imagen anterior se observa como al cdigo que maneja excepciones le coloque antes de cada lnea la doble diagonal // para que sea un comentario, realmente el cdigo del botn es el siguiente: private void button1_Click(object sender, EventArgs e) { try { double x = Double.Parse(textBox1.Text); double y = Convert.ToDouble(textBox2.Text); textBox3.Text = Convert.ToString(x / y); } catch (FormatException) { MessageBox.Show("No es un valor numrico"); MessageBox.Show("Ingrese nuevamente el valor..."); } } De esta manera ya con el manejo de las excepciones la ejecucin del programa sera:
6.8
Con esta ejecucin, controlando el manejo de excepciones podemos afirmar que nuestro programa termina de manera normal, de hecho en cualquier proyecto que elaboremos debemos siempre considerar las condiciones anormales que pudiesen suceder y controlar las excepciones, por ejemplo otra condicin comn que produce una situacin anormal es cuando dividimos cualquier cantidad entre cero o cuando el valor es muy grande, modifiquemos el mismo proyecto agregando un botn ms de la siguiente manera:
El botn Dividir llevara el siguiente cdigo: private void button1_Click(object sender, EventArgs e) { try { int x = Convert.ToInt32(textBox1.Text); int y = Convert.ToInt32(textBox2.Text); textBox3.Text = Convert.ToString(x / y); } catch (FormatException) { MessageBox.Show("No es un valor vlido!!");
6.9 Captulo 6:Control de Excepciones y Archivos
} catch (OverflowException) { MessageBox.Show("Es un valor muy grande!!"); } catch (DivideByZeroException) { MessageBox.Show("No se puede dividir entre cero!!"); } } El botn Multiplicar tendra el siguiente cdigo: private void button2_Click(object sender, EventArgs e) { try { int x = Convert.ToInt32(textBox1.Text); int y = Convert.ToInt32(textBox2.Text); textBox3.Text = Convert.ToString(x * y); } catch (FormatException) { MessageBox.Show("No es un valor vlido"); } catch (OverflowException) { MessageBox.Show("El valor es muy grande"); } }
6.10
6.11
Archivos
En todos los programas, la manera de almacenar y recuperar informacin que perdure en el tiempo se basa en el uso de memoria secundaria, compuesta esencialmente por discos (diskettes, discos duros, CD, DVD, etc.) y ocasionalmente cintas. En cualquiera de estos medios, la unidad de almacenamiento de informacin se denomina archivo. Al igual que prcticamente todos los lenguajes de programacin, C# ofrece el acceso a leer y escribir archivos en disco, por medio de estructuras especialmente definidas. En el caso de C#, las estructuras consisten en clases de caractersticas y mtodos particulares, que resuelven la problemtica general de acceso a los archivos, permitiendo desarrollar programas que los utilicen como fuente de informacin y medio de almacenamiento de informacin de largo plazo.
Streams
Un stream es como se denomina a un objeto utilizado para transferir datos. Estos datos pueden ser transferidos en dos posibles direcciones: - Si los datos son transferidos desde una fuente externa al programa, entonces se habla de leer desde el stream. - Si los datos son transferidos desde el programa a alguna fuente externa, entonces se habla de escribir al stream. Frecuentemente, la fuente externa ser un archivo, pero eso no necesariamente es el caso, por lo que el concepto es utilizado ampliamente con fuentes de informacin externas de diversos tipos. Algunas otras posibilidades fuera de los archivos incluyen: Leer o escribir datos a una red utilizando algn protocolo de red, donde la intencin es que estos datos sean recibidos o enviados por otra computadora. Lectura o escritura a un rea de memoria. La Consola La Impresora Algunas clases que C# provee para resolver este acceso a fuentes diversas incluyen las clases de tipo Reader y Writer.
6.12
Lectura de Archivos
Las clases ms relacionadas con la escritura y lectura de archivos (File Input/Output o File I/O) son: - FileStream, cuyo propsito es lectura y escritura de datos binarios (no de texto legible), a cualquier archivo de tipo binario, aunque se puede utilizar para acceder a cualquier tipo de archivo, inclusive los de texto. - StreamReader y StreamWriter, las cuales estn diseadas para lectura y escritura de archivos de texto. Estas clases se asumen como de un nivel ms alto que FileStream. Una observacin acerca de la declaracin de nombres/rutas de archivos en C#. Usualmente, la ruta de un archivo contiene el carcter \, que en C# se utiliza como caracter de control para smbolos especiales (como el cambio de lnea: \n). Sin embargo, entendiendo que no es el mismo sentido el que se le quiere dar en la interpretacin de rutas de archivos (por ej: C: \Mis documentos\Programas\ejemplo.cs), se utiliza una sintaxis particular, anteponiendo el smbolo @ antes del string con la ruta del archivo. Es decir: string rutaarchivo = @"C:\Temp\archivo.txt"; Esta declaracin evita la interpretacin de los dos caracteres \ como smbolos especiales y el string queda correctamente inicializado.
Using System.IO
Para el uso de estas clases, es necesario referenciar el uso del namespace System.IO, ya que System no contiene los elementos para el manejo de archivos. Por ello, los programas con acceso a archivos deben incluir la lnea: using System.IO;
Control de Errores
Si bien en una serie de instrucciones de ejecucin en lenguaje C# se pueden producir errores importantes, particularmente cuando hay input del usuario que debera tener cierta forma o valores, o en clculos matemticos (especficamente evitando divisiones por cero), es factible en C# tener cierto control sobre el comportamiento del programa ante dichas situaciones.
6.13
Esencialmente, el bloque try-catch persigue precisamente tener cierto control sobre lo que el programa har en caso de producirse un error. public int division(int n1, int n2) { int resultado = 0; try { resultado = n1/n2; } catch(Exception e) { Console.WriteLine("Error en la divisin de {0}/{1}\n\n{2}",n1, n2, e.ToString()); } }
Lectura: StreamReader
La ventaja de esta clase es que hace una operacin sobre archivos que resulta muy natural al momento de utilizarla.
Constructores de StreamReader
El ms simple de los constructores toma slo el nombre/ruta del archivo a abrir para lectura: StreamReader sr = new StreamReader(@ C:\Temp\archivo.txt); Sin embargo, reconociendo que hoy existen diferentes formatos (codificaciones) de archivos de texto y no solamente el tradicional formato ASCII, es factible establecer cul es la codificacin especial que este archivo de texto plano puede tener. Los formatos posibles son: ASCII, Unicode, UTF7, UTF8, BigEndianUnicode. El constructor es: StreamReader sr = new StreamReader(@ C:\Temp\file.txt, Encoding.UTF8Encoding); En trminos prcticos, nos ser necesario recurrir a este tipo codificaciones, ya que usualmente se trabajar con codificacin ASCII. de
El constructor deja abierto el stream para poder recuperar la informacin del archivo desde la instancia de StreamReader declarada. Para cerrar un stream o archivo, se invoca el mtodo Close(): sr.Close();
6.14
6.15
Escritura: StreamWriter
Esta clase funciona prcticamente de la misma manera que StreamReader, excepto que su propsito es nicamente para escribir dentro de un archivo (u otro stream). Es relevante distinguir que en este caso, el proceso de apertura para escritura considera que: - Si el archivo no existe lo crea vaco para comenzar a escribir. - Si el archivo ya existe, lo deja vaco para comenzar a escribir. - Si el archivo ya existe, es posible abrirlo en forma Append (agregar) para escribir al final.
Constructores de StreamWriter
El ms simple de los constructores toma slo el nombre/ruta del archivo a abrir para escritura. StreamWriter sw = new StreamWriter (@C:\Temp\archivo.txt); Este constructor asume por defecto el formato UTF8 de archivos planos, ya que es el manejado por .NET. Sin embargo, existe el constructor equivalente que permite abrir un archivo especificando otra codificacin de archivo plano, por ejemplo ASCII. StreamWriter sw = new StreamWriter (@C:\doc\file.txt, Encoding.ASCII); Un tercer constructor utiliza como segundo parmetro un boolean que indica si el archivo debe ser abierto para Agregar, es decir, en un modo Append. StreamWriter sw = new StreamWriter (@C:\Temp\archivo.txt, true); De la misma manera que en el caso de la lectura, para cerrar un stream o archivo, se invoca el mtodo close. sw.Close();
6.16
Totalmente equivalente a Console.WriteLine(), se utiliza la misma idea, y el mismo formato, sabiendo que se estar escribiendo el texto no a la consola, sino que al stream abierto con el constructor. string linea = Texto de prueba; sw.WriteLine(linea); sw.WriteLine(Los valores posibles son: {0} y {1}, 3, 5); Write () Tambin presente, el mtodo simple Write(), permite escribir texto en el stream, de la misma forma que su equivalente mtodo de la clase Console. En este caso se reconocen las siguientes alternativas de uso: Imprimir un string string linea = Texto de prueba; sw.Write(linea); Imprimir un carcter char caracter = T; sw.Write(caracter); Imprimir un arreglo de caracteres char[] caracteres = new char[100]; for(int i=0; i<100; i++) caracteres[i] = +; sw.Write(caracteres); Imprimir una porcin de un arreglo de caracteres char[] caracteres = new char[100]; for(int i=0; i<100; i++) caracteres[i] = +; sw.Write(caracteres, 25, 50); // Desde posicin 25 se escriben 50 caracteres
Ejemplos de Archivos
El siguiente ejemplo ilustra cmo podemos leer el contenido de un archivo que ya existe y muestra cmo podemos escribir en un archivo, si el archivo no existe se crea automticamente. Para tal fin debemos de tener grabado en C:\ o en la carpeta que queramos el archivo uacm.txt como se puede observar en la siguiente figura el contenido del archivo puede variar.
6.17
E proyecto ser del tipo Consola, el cdigo fuente correspondiente sera: Archivos1.cs
// Archivos1.cs using System; using System.IO; class Archivos { static void Main(string[] args) { int contador = 0; string lineas; // Lee el archivo y muestra su contenido lnea por lnea StreamReader archivo1 = new StreamReader(@"c:\uacm.txt"); while ((lineas = archivo1.ReadLine()) != null) { Console.WriteLine(lineas); contador++; } archivo1.Close(); //Cierra el archivo1 Console.WriteLine("\nFueron {0} lneas leidas!!", contador); Console.ReadLine(); // Se crea una cadena que consiste en tres lneas string informacion = "\n Es necesario referenciar el uso del namespace System.IO \n Ya que System no contiene los elementos para el manejo de archivos \nIncluir la linea: using System.IO;"; // Escribe la cadena informacin en archivo2 StreamWriter archivo2 = new StreamWriter(@"C:\Users\Rosalba\Documents\avalera.doc"); //Si no existe se crea el archivo avalera.doc, //verificar permisos para escribir en C:\ o la ruta que deseemos archivo2.WriteLine(informacion); archivo2.Close(); } }
Al ejecutar el archivo se mostrara la siguiente pantalla, despus de dar enter se cerrara el archivo.
6.18
Posteriormente grabara en el archivo avalera.doc la cadena de tres lneas si no existe el archivo lo crea, si ya existe borra la informacin existente y graba la cadena de informacin, Con el Explorador de Windows podemos ir a C:\ o a la ruta que elegimos para ver el archivo, al abrirlo comprobaremos que efectivamente grabo la informacin:
Si queremos grabar en C:\ o alguna otra ruta y no tenemos los permisos para realizarlos se generara el siguiente error:
6.19
6.20
sr.Close(); } } class MainApp { static void Main() { string nombre; Console.Write("Nombre del archivo: "); nombre = Console.ReadLine(); Calculadora c = new Calculadora(nombre);
c.Procesar();
Console.ReadLine(); } }
El archivo clculos.txt deber terminar con un enter al final de la ltima lnea y deber estar guardado en la carpeta: ...nombredelproyecto/bin/Debug
6.21
Del lado izquierdo se muestra la ejecucin cuando damos una dimensin de 5 para nuestra matriz cuadrada en esta parte cada elemento de la matriz resultante se inicializa en cero, del lado derecho cuando damos clic en Suma obtenemos el resultado de cada elemento de la matriz resultante.
6.22