Examinar cómo crear y lanzar excepciones en C#
- 16 minutos
.NET proporciona una jerarquía de clases de excepción que derivan de la System.Exception clase base. Las aplicaciones de C# pueden crear y lanzar excepciones de cualquier tipo de excepción. Los desarrolladores también pueden personalizar objetos de excepción con información específica de la aplicación asignando valores de propiedad.
Nota:
Este módulo se centra en la creación y el lanzamiento de excepciones y la personalización de objetos de excepción. La creación de clases de excepciones personalizadas está fuera del ámbito de este módulo.
Creación de un objeto de excepción
Crear y lanzar excepciones dentro de tu código es un aspecto importante de la programación de C#. La capacidad de generar una excepción en respuesta a una condición, un problema o un error específicos le ayuda a garantizar la estabilidad de la aplicación.
El tipo de excepción que cree depende del problema de codificación y debe coincidir con el propósito previsto de la excepción lo más cerca posible.
Por ejemplo, supongamos que va a crear un método denominado GraphData que realiza el análisis de datos. El método recibe una matriz de datos como parámetro de entrada. El método espera que los datos de entrada estén en un intervalo determinado. Si el método recibe datos que están fuera del intervalo esperado, crea e inicia una excepción de tipo ArgumentException. El código encargado de proporcionar los datos manejará la excepción en algún punto de la pila de llamadas.
Estos son algunos tipos de excepciones comunes que puede usar al crear una excepción:
ArgumentExceptionoArgumentNullException: use estos tipos de excepción cuando se llama a un método o constructor con un valor de argumento no válido o una referencia nula.InvalidOperationException: use este tipo de excepción cuando las condiciones de funcionamiento de un método no admitan la finalización correcta de una llamada de método determinada.NotSupportedException: use este tipo de excepción cuando no se admita una operación o característica.IOException: use este tipo de excepción cuando se produzca un error en una operación de entrada y salida.FormatException: use este tipo de excepción cuando el formato de una cadena o datos sea incorrecto.
La new palabra clave se usa para crear una instancia de una excepción. Por ejemplo, puede crear una instancia del tipo de excepción ArgumentException de la siguiente manera:
ArgumentException invalidArgumentException = new ArgumentException();
Configurar y lanzar excepciones personalizadas
El proceso para iniciar un objeto de excepción implica crear una instancia de una clase derivada de excepciones, configurar opcionalmente las propiedades de la excepción y, a continuación, iniciar el objeto mediante la throw palabra clave .
A menudo resulta útil personalizar una excepción con información contextual antes de que se produzca. Puede proporcionar información específica de la aplicación dentro de un objeto de excepción configurando sus propiedades. Por ejemplo, el código siguiente crea un objeto de excepción denominado invalidArgumentException con una propiedad personalizada Message y, a continuación, produce la excepción:
ArgumentException invalidArgumentException = new ArgumentException("ArgumentException: The 'GraphData' method received data outside the expected range.");
throw invalidArgumentException;
Nota:
La propiedad Message de una excepción es de solo lectura. Por lo tanto, se debe establecer una propiedad personalizada Message al crear una instancia del objeto .
Al personalizar un objeto de excepción, es importante proporcionar mensajes de error claros que describen el problema y cómo resolverlo. También puede incluir información adicional, como trazas de pila y códigos de error para ayudar a los usuarios a corregir el problema.
También se puede crear un objeto de excepción directamente dentro de una throw instrucción . Por ejemplo:
throw new FormatException("FormatException: Calculations in process XYZ have been cancelled due to invalid data format.");
Algunas consideraciones que se deben tener en cuenta al iniciar una excepción incluyen:
- La
Messagepropiedad debe explicar el motivo de la excepción. Sin embargo, la información confidencial o que representa un problema de seguridad no debe colocarse en el texto del mensaje. - La
StackTracepropiedad se usa a menudo para realizar un seguimiento del origen de la excepción. Esta propiedad de cadena contiene el nombre de los métodos de la pila de llamadas actual, junto con el nombre de archivo y el número de línea de cada método asociado a la excepción. Un objetoStackTracees creado automáticamente por el Common Language Runtime (CLR) desde el punto de la instrucciónthrow. Las excepciones deben iniciarse desde el punto donde debe comenzar el seguimiento de la pila.
Cuándo lanzar una excepción
Los métodos deben producir una excepción siempre que no puedan completar su propósito previsto. La excepción lanzada debe basarse en la excepción más específica disponible que se ajuste a las condiciones de error.
Considere un escenario en el que un desarrollador está trabajando en una aplicación que implementa un proceso de negocio. El proceso de negocio depende de la entrada del usuario. Si la entrada no coincide con el tipo de datos esperado, el método que implementa el proceso de negocio crea y produce una excepción. El objeto de excepción se puede configurar con información específica de la aplicación en los valores de sus propiedades. En el ejemplo de código siguiente se muestra el escenario:
string[][] userEnteredValues = new string[][]
{
new string[] { "1", "two", "3"},
new string[] { "0", "1", "2"}
};
foreach (string[] userEntries in userEnteredValues)
{
try
{
BusinessProcess1(userEntries);
}
catch (Exception ex)
{
if (ex.StackTrace.Contains("BusinessProcess1") && (ex is FormatException))
{
Console.WriteLine(ex.Message);
}
}
}
static void BusinessProcess1(string[] userEntries)
{
int valueEntered;
foreach (string userValue in userEntries)
{
try
{
valueEntered = int.Parse(userValue);
// completes required calculations based on userValue
// ...
}
catch (FormatException)
{
FormatException invalidFormatException = new FormatException("FormatException: User input values in 'BusinessProcess1' must be valid integers");
throw invalidFormatException;
}
}
}
En este ejemplo de código, las instrucciones de nivel superior llaman al BusinessProcess1 método , pasando una matriz de cadenas que contiene valores especificados por el usuario. El BusinessProcess1 método espera valores de entrada de usuario que se pueden convertir en un entero. Cuando el método encuentra datos con un formato no válido, crea una instancia del FormatException tipo de excepción mediante una propiedad personalizada Message . A continuación, el método lanza la excepción. La excepción se detecta en las instrucciones de nivel superior como un objeto denominado ex. Las propiedades del ex objeto se examinan antes de mostrar el mensaje de excepción al usuario. En primer lugar, el código examina la StackTrace propiedad para ver si contiene "BusinessProcess1". En segundo lugar, se comprueba que el objeto ex de excepción es de tipo FormatException.
Volver a generar excepciones
Además de iniciar una nueva excepción, throw se puede usar para volver a iniciar una excepción desde dentro de un catch bloque de código. En este caso, throw no toma un operando de excepción.
catch (Exception ex)
{
// handle or partially handle the exception
// ...
// re-throw the original exception object for further handling down the call stack
throw;
}
Cuando se vuelve a producir una excepción, se usa el objeto de excepción original, por lo que no se pierde información sobre la excepción. Si desea crear un nuevo objeto de excepción que encapsula la excepción original, puede pasar la excepción original como argumento al constructor de un nuevo objeto de excepción. Por ejemplo:
catch (Exception ex)
{
// handle or partially handle the exception
// ...
// create a new exception object that wraps the original exception
throw new ApplicationException("An error occurred", ex);
}
Para el escenario de la aplicación "BusinessProcess1", tenga en cuenta las siguientes actualizaciones:
- El
BusinessProcess1método se ha actualizado para incluir detalles adicionales.BusinessProcess1ahora encuentra dos problemas y debe generar excepciones para cada problema. - Se han actualizado las declaraciones de nivel superior. Las instrucciones de nivel superior ahora llaman al método
OperatingProcedure1.OperatingProcedure1llama aBusinessProcess1dentro de untrybloque de código. - El
OperatingProcedure1método puede controlar uno de los tipos de excepción y controlar parcialmente el otro. Una vez procesada la excepción controlada parcialmente,OperatingProcedure1debe volver a iniciar la excepción original.
En el ejemplo de código siguiente se muestra el escenario actualizado:
try
{
OperatingProcedure1();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine("Exiting application.");
}
static void OperatingProcedure1()
{
string[][] userEnteredValues = new string[][]
{
new string[] { "1", "two", "3"},
new string[] { "0", "1", "2"}
};
foreach(string[] userEntries in userEnteredValues)
{
try
{
BusinessProcess1(userEntries);
}
catch (Exception ex)
{
if (ex.StackTrace.Contains("BusinessProcess1"))
{
if (ex is FormatException)
{
Console.WriteLine(ex.Message);
Console.WriteLine("Corrective action taken in OperatingProcedure1");
}
else if (ex is DivideByZeroException)
{
Console.WriteLine(ex.Message);
Console.WriteLine("Partial correction in OperatingProcedure1 - further action required");
// re-throw the original exception
throw;
}
else
{
// create a new exception object that wraps the original exception
throw new ApplicationException("An error occurred - ", ex);
}
}
}
}
}
static void BusinessProcess1(string[] userEntries)
{
int valueEntered;
foreach (string userValue in userEntries)
{
try
{
valueEntered = int.Parse(userValue);
checked
{
int calculatedValue = 4 / valueEntered;
}
}
catch (FormatException)
{
FormatException invalidFormatException = new FormatException("FormatException: User input values in 'BusinessProcess1' must be valid integers");
throw invalidFormatException;
}
catch (DivideByZeroException)
{
DivideByZeroException unexpectedDivideByZeroException = new DivideByZeroException("DivideByZeroException: Calculation in 'BusinessProcess1' encountered an unexpected divide by zero");
throw unexpectedDivideByZeroException;
}
}
}
El código de ejemplo actualizado genera la siguiente salida:
FormatException: User input values in 'BusinessProcess1' must be valid integers
Corrective action taken in OperatingProcedure1
DivideByZeroException: Calculation in 'BusinessProcess1' encountered an unexpected divide by zero
Partial correction in OperatingProcedure1 - further action required
DivideByZeroException: Calculation in 'BusinessProcess1' encountered an unexpected divide by zero
Exiting application.
Cosas que se deben evitar al lanzar excepciones
En la lista siguiente se identifican las prácticas que se deben evitar al iniciar excepciones:
- No use excepciones para cambiar el flujo de un programa como parte de la ejecución ordinaria. Use excepciones para notificar y controlar las condiciones de error.
- Las excepciones no se deben devolver como un valor devuelto o un parámetro en lugar de iniciarse.
- No inicie
System.Exception,System.SystemException,System.NullReferenceExceptionoSystem.IndexOutOfRangeExceptionde manera intencional desde su propio código fuente. - No cree excepciones que se puedan producir en modo de depuración, pero no en modo de versión. Para identificar errores en tiempo de ejecución durante la fase de desarrollo, use
Debug.Asserten su lugar.
Nota:
El Debug.Assert método es una herramienta para detectar errores lógicos durante el desarrollo. De forma predeterminada, el Debug.Assert método solo funciona en compilaciones de depuración. Puede usar Debug.Assert en sesiones de depuración para comprobar si hay una condición que nunca debe producirse. El método toma dos parámetros: una condición booleana para comprobar y un mensaje de cadena opcional para mostrar si la condición es false. Debug.Assert no debe usarse en lugar de lanzar una excepción, que es una manera de controlar situaciones excepcionales durante la ejecución normal del código. Debe usar Debug.Assert para detectar errores que nunca deben producirse y usar excepciones para controlar los errores que podrían producirse durante la ejecución normal del programa.
Resumen
Estos son algunos de los aspectos más importantes que debe recordar de esta unidad:
- Al crear y producir una excepción, el tipo de excepción debe coincidir con el propósito previsto de la excepción lo más cerca posible.
- Para una excepción
throw, se crea una instancia de una clase derivada de excepción, se configuran sus propiedades y, a continuación, se usa la palabra clavethrow. - Al crear un objeto de excepción, es importante proporcionar mensajes de error claros e información adicional para ayudar a los usuarios a corregir el problema.