Si escribe software para ganarse la vida en 2018, es probable que al menos esté familiarizado con el concepto de excepciones.
Jeff Atwood una vez los llamó "el pan y la mantequilla de los lenguajes de programación modernos". Las excepciones son una construcción común y útil presente en el desarrollo de software moderno, pero a veces también pueden ser bastante confusas.
¿Qué son las excepciones, después de todo? Más específicamente, ¿cuáles son los principales tipos de excepciones de C # y cómo los usa?
La publicación de hoy responderá a las preguntas anteriores, y más. Comenzaremos con una breve definición de "excepción" y procederemos a explicar las partes compuestas de la estructura. Finalmente, haremos una descripción general de las excepciones más comunes de C # y cómo tratarlas.
Empecemos.
¿Qué es una excepción?
Una excepción es un mecanismo que puede utilizar para tratar los errores. Es así de simple. En ciertos escenarios donde, digamos, un programador de C devolvería un código de error, lo más probable es que un programador de Java o C # lance una excepción.
Una excepción representa una interrupción abrupta del flujo de ejecución. Tan pronto como se lanza una excepción, la ejecución se detiene. Si la excepción no se maneja, la aplicación se bloquea.
Pero, ¿cómo se hace eso en la práctica? ¿Cómo tirar o atrapar una excepción? ¿Qué significa todo eso?
Eso es lo que veremos en detalle en la siguiente sección.
Anatomía de las excepciones de C #
Ahora cubriremos brevemente la anatomía de una excepción de C #. Aprenderá sobre las palabras clave principales que debe usar no solo para detectar y manejar excepciones, sino también para lanzar las suyas propias. Primero en nuestra lista es la
try
palabra clave y el bloque.Tratar
La primera parte de la anatomía del manejo de excepciones es el
try
bloque. Se usa para tratar de ejecutar algún código que puede generar excepciones. Considere el siguiente extracto de código:string content = string.Empty;
try
{
content = System.IO.File.ReadAllText(@"C:\file.txt");
}
El código anterior declara una variable y le asigna la cadena vacía. Entonces, tenemos el
try
bloque. La única línea de código dentro del try
bloque usa el ReadAllText
método estático de la System.IO.File
clase. Tememos que el archivo representado por la ruta no exista, en cuyo caso se producirá una excepción. Pero por supuesto, eso no es suficiente. Nuestro código no hace nada para manejar la excepción. ¡Ni siquiera compila actualmente! Este es el mensaje del compilador:Expected catch or finally
El mensaje es autoexplicativo. Necesitamos un bloque
catch
o finally
ya que no tiene ningún sentido tratar de manejar una excepción y luego olvidarse de hacer la parte de manejo. Vamos a eso.Captura
El
catch
bloque es lo que nos permite manejar la excepción. Ampliaremos el ejemplo anterior con más código.Echale un vistazo:
static void Main(string[] args)
{
string content = string.Empty;
try
{
Console.WriteLine("First message inside try block.");
Console.WriteLine("Second message inside try block.");
content = System.IO.File.ReadAllText(@"C:\file.txt");
Console.WriteLine("Third message inside try block. This shouldn't be printed.");
}
catch
{
Console.WriteLine("First message inside the catch block.");
Console.WriteLine("Second message inside the catch block.");
}
Console.WriteLine("Outside the try-catch.");
Console.ReadLine();
}
Si ejecuta el código anterior, esto es lo que debería ver:
First message inside try block.
Second message inside try block.
First message inside the catch block.
Second message inside the catch block.
Outside try-catch.
El archivo en esa ruta no existe, por lo que
System.FileNotFoundException
se lanza un. El flujo de ejecución se interrumpe inmediatamente cuando eso sucede. Así que la línea que imprimiría "Tercer mensaje dentro del bloque try. Esto no debería ser impreso ". Nunca se ejecuta.El flujo de ejecución es recogido por el
catch
bloque, que ejecuta sus dos líneas. Cuando termina, devuelve el control al método principal, que luego imprime Outside try-catch
. y espera la entrada del usuario.Finalmente
Después de aprender sobre
try
y catch
, finalmente estamos (sin ningún juego de palabras) listos para asimilar esta parte muy útil pero a menudo mal entendida del mecanismo de manejo de excepciones. Entonces, ¿qué es finally
?El
finally
bloque es una forma de asegurarse de que se va a ejecutar un determinado fragmento de código, independientemente de si se lanza una excepción o no. Considere el siguiente código:try
{
content = System.IO.File.ReadAllText(path);
Console.WriteLine("If you're reading this, no exception was thrown.");
}
catch (System.IO.FileNotFoundException e)
{
Console.WriteLine("If you're reading this, an exception was thrown.");
Console.WriteLine("Message: " + e.Message);
}
finally
{
Console.WriteLine("This will be printed, no matter what.");
}
Sigue siendo el mismo ejemplo, pero ahora es mucho más simple. Fíjate en el
finally
bloque al final. Si ejecuta este código, debería ver los siguientes mensajes:If you're reading this, an exception was thrown.
Message: Could not find file 'C:\file.txt'.
This will be printed, no matter what.
Outside the try-catch.
Ahora simulemos la existencia del archivo. Comente la línea tratando de leer el archivo y vuelva a ejecutar la aplicación. Esto es lo que deberías ver ahora:
If you're reading this, no exception was thrown.
This will be printed, no matter what.
Outside the try-catch.
Como puede ver, en el escenario más reciente, la excepción no fue lanzada, por lo que no se ejecutaron líneas en el
catch
bloque. Por otro lado, el finally
bloque se ejecutó en ambos escenarios.Lanzar
Cuando se trata de excepciones, no siempre las manejarás desde otros; También puedes lanzar el tuyo. Para eso, va a utilizar la
throw
palabra clave, seguida de una instanciación de la clase para la excepción que desea lanzar. El siguiente código ejemplifica esto:public ProductService(IProductRepository repository)
{
if (repository == null)
throw new ArgumentNullException();
this.repository = repository;
}
Excepciones comunes de .NET
La siguiente es una lista de excepciones comunes de .NET:
System.NullReferenceException
Esta es una de las excepciones más famosas (o más bien infames) que existen. Esta excepción se produce cuando intenta llamar a un método / propiedad / indexador / etc. en una variable que contiene una referencia nula, es decir, no apunta a ningún objeto. El siguiente código causará una excepción de referencia nula:
Person p = people.Where(x => x.SSN == verifySsn).FirstOrDefault();
string name = p.Name;
En el ejemplo anterior, filtramos una secuencia que compara la propiedad SSN en cada elemento con la
verifySsnvariable
variable. Luego usamos el FirstOrDefault()
método para tomar solo el primer elemento de la secuencia. Si la secuencia no produce ningún elemento, devuelve el valor predeterminado para el tipo. Dado que Person
es un tipo de referencia, su valor de retorno es nulo.En la siguiente línea, intentamos anular la referencia de la
Name
propiedad en una referencia nula. ¡Auge! Excepcion de referencia nula.Esta es una excepción que usualmente ni tiras ni atrapas. No lo lanzas porque sería inútil. Si desea comunicar a los clientes de su código que un método determinado no acepta el valor nulo como valores válidos para sus parámetros, la excepción correcta a usar es la
System.ArgumentNullException
.¿Cómo “arreglas” esta excepción? En resumen, debe ser cuidadoso y diligente sobre la nulabilidad. Si está escribiendo un fragmento de código que será utilizado por terceros, incluso si esos terceros son sus compañeros de trabajo, piense muy bien si aceptar o no las referencias nulas como valores válidos.
Decida lo que decida, tome esa decisión muy clara y documéntela bien. Lo haces lanzando
System.ArgumentNullException
, por ejemplo, y también usando encabezados de documentación XML en tus métodos.System.IndexOutOfRangeException
Esta excepción es similar a la anterior en el sentido de que el código de la aplicación normalmente no lo lanzaría ni lo atraparía.
¿Y por qué es eso?
Bueno, esta excepción se produce cuando intentas acceder a un elemento desde una matriz, lista o cualquier secuencia indexable utilizando un valor de índice no válido. Un ejemplo rápido:
public static void PrintUrlSufix(string url)
{
var parts = url.Split('.');
Console.WriteLine(parts[2]);
}
El código anterior aparentemente espera una URL en el formato www.acme.com . Pero ¿y si solo se pone acme.com ? Para el caso, ¿qué pasa si se Hakuna Matata ? Sí, es cierto:
System.IndexOutOfBoundException
lo es.¿Cómo se evita esta excepción? Nunca tomes las cosas por sentado. Nunca asuma que los datos estarán en el formato correcto. Haga su diligencia debida y revise las cosas.
System.IO.IOException
Esta excepción de C # tiene un nombre que se explica por sí mismo. Es exactamente lo que pensaría: es la excepción que se produce cuando las cosas salen mal durante las operaciones de IO. A diferencia de las dos excepciones anteriores, es posible que se encuentre atrapando o lanzando una de estas de vez en cuando.
La
IOException
clase es en realidad el padre de algunas excepciones más específicas, a saber:- DirectoryNotFoundException
- EndOfStreamException
- FileNotFoundException
- FileLoadException
- PathTooLongException
La documentación para
IOException
recomienda que, siempre que sea posible, utilice las excepciones más específicas en lugar de la más general.System.Net.WebException
Esta excepción está relacionada con la red. Se produce si se produce un error al acceder a la red mediante un protocolo conectable. Al manejar esta excepción, recuerde verificar la
Response
propiedad, que contendrá la respuesta devuelta por el host remoto.System.Data.SqlClient.SqlException
Esta excepción está relacionada con la base de datos, específicamente, con SQL Server. Se lanza cuando SQL Server devuelve un error o una advertencia. La clase tiene una propiedad llamada
Errors
, que es una colección que contiene una o más instancias de la SqlError
clase. Eso, a su vez, contiene información detallada sobre los errores que ocurrieron.System.StackOverflowException
Esta excepción se produce cuando la pila de ejecución se desborda, lo que generalmente significa que la recursión salió mal. El código tiene demasiadas llamadas de método anidadas. Y aquí está la cosa: esta excepción no es detectable, al menos no desde .NET 2.0, lo que significa que prácticamente no tienes alternativas cuando se lanza esta excepción. Su proceso será terminado de forma predeterminada.
Lo que debe hacer en lugar de capturar esta excepción es escribir código que evite que suceda en primer lugar.
System.OutOfMemoryException
Esta es posiblemente una de las excepciones de C # más confusas que existen . Hay recursos en la web que hacen un gran trabajo para aclarar el problema , pero ofreceré una versión corta aquí. Lo que sucede es que esta excepción no se refiere a la memoria física disponible.
Entonces, ¿cuándo se lanza esta excepción?
¿Sabe cuándo quiere estacionar su automóvil y no puede porque otros conductores no han estacionado correctamente? Si pudiera agregar todos los espacios disponibles entre los autos estacionados, sería más que suficiente para adaptarse a su vehículo. Pero actualmente, no es posible porque no es un área contigua.
Eso es más o menos lo que sucede cuando obtienes este recuerdo. Puede haber un montón de memoria total disponible, pero no hay una parte continua para cumplir con la asignación necesaria. En la práctica, esta excepción ocurre, por ejemplo, si intenta expandir una propiedad
StringBuilder
más allá de su MaxCapacity
propiedad.System.InvalidCastException
Esta excepción también tiene un nombre que se explica por sí mismo. Se lanza cuando el código no realiza una conversión de un tipo a otro porque no hay una conversión definida. El siguiente código arrojaría una excepción de este tipo:
object o = "10";
int x = (int)o;
Esta es una excepción que normalmente no atraparía. Más bien, escribirías tu código de tal manera que no suceda. Por ejemplo, el siguiente código realiza una verificación de tipo antes de intentar realizar el lanzamiento:
public override bool Equals (object obj )
{
if (!obj is Foo)
return false;
Foo other = (Foo)obj;
return this.bar == other.bar;
}
El código anterior podría simplificarse utilizando el
as
operador , pero lo dejaré como un ejercicio para el lector. De todos modos, aquí está la cosa: lo que realmente deberías estar tratando de hacer es evitar el problema al no lanzar en primer lugar. Utilice los genéricos para evitar meterse en situaciones que necesitan ser lanzados.System.InvalidOperationException
Esta excepción, como muchas otras anteriores, es una que normalmente no atraparía. En cambio, lo que debes hacer es codificar de tal manera que no suceda. Considere el siguiente ejemplo:
var numbers = new List<int> { 1, 3, 5 };
var firstGreaterThanFive = numbers.Where(x => x > 5).First();
El método de extensión First LINQ se produce cuando la secuencia no genera ningún resultado. Si sabe que la secuencia puede no producir resultados a veces, y eso está bien, debería usar FirstOrDefault en su lugar. Este método, cuando se llama en una secuencia vacía, devuelve el valor predeterminado para el tipo de secuencia en lugar de lanzar.
System.ObjectDisposedException
La última excepción de C # que cubriremos en esta publicación también cae en la categoría de "no debería manejar, corregir su código". En otras palabras, es un error del desarrollador. Esta excepción se produce cuando intenta hacer algo con un IDisposible que ya se eliminó.
Esto suele suceder cuando el desarrollador llama al
Dispose
, Close
u otro método similar y luego intenta acceder a un miembro del objeto.Manejo de errores para la victoria.
El manejo de errores es un tema que a menudo se pasa por alto cuando se trata de la enseñanza del desarrollo de software. Y eso es lo más desafortunado. Sin una sólida estrategia de manejo de errores , sus aplicaciones siempre serán insatisfactorias.
Con esta publicación, esperamos haber hecho un poco para ayudar a solucionar este problema definiendo el concepto de excepción y haciendo una descripción general rápida de los principales tipos de excepciones de C #. ¿Esto es todo lo que hay para el manejo de excepciones?
De ninguna manera. Piense en esto como una oportunidad para comenzar a poner en marcha sus estudios sobre el tema y nunca dejar de aprender y practicar. ¡Y le deseo suerte en la creación de una estrategia que funcione bien para su aplicación!
0 Comentarios