Procedimientos recomendados para controlar excepciones
Actualización: noviembre 2007
Un conjunto bien diseñado de bloques de código de control de errores puede hacer que un programa sea mucho más fuerte y menos propenso a interrupciones, porque la aplicación controla tales errores. A continuación se proporciona una lista de sugerencias sobre los procedimientos recomendados para controlar excepciones:
Saber cuándo configurar un bloque Try/Catch. Por ejemplo, se puede comprobar mediante programación si se da una condición que es probable que ocurra sin utilizar el control de errores. En otras situaciones, es aconsejable utilizar el control de excepciones para detectar una condición de error.
En el ejemplo siguiente se usa una instrucción if para comprobar si se ha cerrado una conexión. Se puede usar este método en lugar de producir una excepción si la conexión no está cerrada.
If conn.State <> ConnectionState.Closed Then conn.Close() End If
if(conn.State != ConnectionState.Closed) conn.Close();
En el ejemplo siguiente, se produce una excepción si la conexión no está cerrada.
Try conn.Close() Catch ex As InvalidOperationException 'Do something with the error or ignore it. End Try
try { conn.Close(); } catch(InvalidOperationException ex) { //Do something with the error or ignore it. }
El método que se elija depende de la frecuencia con que se espera que se produzca el evento. Si el evento es muy excepcional y es un error (por ejemplo, un final de archivo inesperado), es mejor utilizar el control de excepciones porque se ejecuta menos código en el caso normal. Si el evento ocurre con frecuencia, es mejor utilizar el método de programación para comprobar si hay errores. En este caso, si se produce una excepción, se tarda más en controlarla.
Utilice bloques Try/Finally en torno a código que podría generar una excepción y centralice las instrucciones Catch en una ubicación. De esta manera, la instrucción Try genera la excepción, la instrucción Finally cierra o desasigna recursos y la instrucción Catch controla la excepción desde una ubicación central.
Ordene siempre las excepciones de los bloques Catch de la más específica a la menos. Con esta técnica se controla la excepción específica antes de que pase a un bloque Catch más general.
Termine los nombres de clases de excepción con la palabra "Exception". Por ejemplo:
Public Class EmployeeListNotFoundException Inherits Exception
public class MyFileNotFoundException : Exception { }
Cuando cree excepciones definidas por el usuario, debe garantizar que los metadatos de las excepciones están disponibles para el código que se ejecute de forma remota, incluso cuando se produzcan excepciones en distintos dominios de aplicación. Por ejemplo, supongamos que el dominio de aplicación A crea el dominio de aplicación B, que ejecuta código que produce una excepción. Para que el dominio de aplicación A detecte y controle la excepción correctamente, debe poder encontrar el ensamblado que contiene la excepción que produce el dominio de aplicación B. Si el dominio de aplicación B produce una excepción contenida en un ensamblado de su base de aplicación pero no de la base de la aplicación del dominio de aplicación A, este último dominio no encontrará la excepción y Common Language Runtime producirá una excepción FileNotFoundException. Para evitar esta situación, puede implementar el ensamblado que contiene la información de la excepción de dos maneras:
Ponga el ensamblado en una base de aplicación compartida por los dos dominios de aplicación.
O bien,
Si los dominios no comparten una base de aplicación común, firme el ensamblado que contiene la información de la excepción con un nombre seguro e impleméntelo en la caché de ensamblados global.
En C# y C++, al menos utilice tres constructores comunes al crear sus propias clases de excepciones. Para obtener un ejemplo, vea Cómo: Crear excepciones definidas por el usuario.
Utilice, en la mayoría de los casos, los tipos de excepción predefinidos. Defina nuevos tipos de excepción sólo para escenarios de programación. Introduzca una nueva clase de excepción que permita al programador adoptar acciones distintas en código basado en la clase de excepción.
Para la mayoría de las aplicaciones, derive excepciones personalizadas de la clase Exception. Al principio, se pensaba que las excepciones personalizadas debían derivarse de la clase ApplicationException; sin embargo, en la práctica, se ha probado que esto no tiene ninguna importancia.
Incluya una cadena descriptiva localizada en todas las excepciones. Cuando el usuario ve un mensaje de error, deriva de la cadena descriptiva de la excepción que se produjo y no de la clase de excepción.
Utilice mensajes de error gramaticalmente correctos, incluida la puntuación de cierre. Cara oración de una cadena descriptiva de una excepción debe acabar con un punto.
Proporcione propiedades de Exception para el acceso mediante programación. Incluya información adicional en una excepción (además de la cadena descriptiva) sólo si hay un escenario de programación en que dicha información es útil.
Devuelva null para los casos de error muy frecuentes. Por ejemplo, Open devuelve null si no se encuentra el archivo, pero produce una excepción si el archivo está bloqueado.
Diseñe las clases de manera que nunca se produzca una excepción en el uso normal. Por ejemplo, una clase FileStream expone otra forma de determinar si se ha llegado al final del archivo. Así se evita la excepción que se produce si se lee más allá del final del archivo. En el ejemplo siguiente se muestra cómo leer hasta el final del archivo.
Class FileRead Public Sub Open(ByVal fileToRead As FileStream) ' This If statement is optional ' as it is very unlikely that ' the stream would ever be null If IsDBNull(fileToRead) Then Throw New System.ArgumentNullException() End If Dim b As Integer ' Set the stream position to the beginning of the file. fileToRead.Seek(0, SeekOrigin.Begin) ' Read each byte to the end of the file. For i As Integer = 0 To fileToRead.Length b = fileToRead.ReadByte() Console.Write(b.ToString()) ' Or do something else with the byte. Next End Sub End Class class FileRead { public void Open(FileStream fileToRead) { // This if statement is optional // as it is very unlikely that // the stream would ever be null. if (fileToRead == null) { throw new System.ArgumentNullException(); } int b; // Set the stream position to the beginning of the file. fileToRead.Seek(0, SeekOrigin.Begin); // Read each byte to the end of the file. for (int i = 0; i < fileToRead.Length; i++) { b = fileToRead.ReadByte(); Console.Write(b.ToString()); // Or do something else with the byte. } } }
Inicie una excepción InvalidOperationException si un conjunto de propiedades o una llamada a un método no resultan apropiados de acuerdo con el estado actual del objeto.
Inicie una excepción ArgumentException o una clase derivada de ArgumentException si se pasan parámetros no válidos.
El seguimiento de pila comienza en la instrucción en que se produce la excepción y termina en la instrucción Catch que detecta la excepción. Tenga esto en cuenta para decidir dónde ubicar una instrucción Throw.
Utilice métodos de generador de excepciones. Es habitual que una clase produzca la misma excepción desde distintos lugares de su implementación. Para evitar el exceso de código, use métodos auxiliares que creen la excepción y la devuelvan. Por ejemplo:
Class File Private fileName As String Public Function Read(bytes As Integer) As Byte() If Not ReadFile(handle, bytes) Then Throw NewFileIOException() End If End Function 'Read Function NewFileIOException() As FileException Dim description As String = __unknown ' Build localized string, including fileName. Return New FileException(description) ' End Function 'NewFileIOException End Class 'File
class File { string fileName; public byte[] Read(int bytes) { if (!ReadFile(handle, bytes)) throw NewFileIOException(); } FileException NewFileIOException() { string description = // Build localized string, including fileName. return new FileException(description); } }
También puede utilizar el constructor de la excepción para generarla. Esto está más indicado para clases de excepción globales, como ArgumentException.
Produzca excepciones en lugar de devolver un código de error o HRESULT.
Cuando produzca una excepción, elimine los resultados intermedios. Los autores de llamadas deben poder asumir que no se producen efectos no deseados cuando se produce una excepción desde un método.