Clase System.Exception
En este artículo se proporcionan comentarios adicionales a la documentación de referencia de esta API.
La Exception clase es la clase base para todas las excepciones. Cuando se produce un error, el sistema o la aplicación que se está ejecutando lo notifica iniciando una excepción que contiene información sobre el error. Después de iniciar una excepción, la controla la aplicación o el controlador de excepciones predeterminado.
Errores y excepciones
Los errores en tiempo de ejecución pueden producirse por diversos motivos. Sin embargo, no todos los errores se deben controlar como excepciones en el código. Estas son algunas categorías de errores que pueden producirse en tiempo de ejecución y las formas adecuadas de responder a ellos.
Errores de uso. Un error de uso representa un error en la lógica del programa que puede dar lugar a una excepción. Sin embargo, el error no debe solucionarse mediante el control de excepciones, sino modificando el código defectuoso. Por ejemplo, la invalidación del método Object.Equals(Object) en el ejemplo siguiente supone que el argumento
obj
siempre debe ser distinto de null.using System; public class Person1 { private string _name; public string Name { get { return _name; } set { _name = value; } } public override int GetHashCode() { return this.Name.GetHashCode(); } public override bool Equals(object obj) { // This implementation contains an error in program logic: // It assumes that the obj argument is not null. Person1 p = (Person1) obj; return this.Name.Equals(p.Name); } } public class UsageErrorsEx1 { public static void Main() { Person1 p1 = new Person1(); p1.Name = "John"; Person1 p2 = null; // The following throws a NullReferenceException. Console.WriteLine("p1 = p2: {0}", p1.Equals(p2)); } }
// In F#, null is not a valid state for declared types // without 'AllowNullLiteralAttribute' [<AllowNullLiteral>] type Person() = member val Name = "" with get, set override this.GetHashCode() = this.Name.GetHashCode() override this.Equals(obj) = // This implementation contains an error in program logic: // It assumes that the obj argument is not null. let p = obj :?> Person this.Name.Equals p.Name let p1 = Person() p1.Name <- "John" let p2: Person = null // The following throws a NullReferenceException. printfn $"p1 = p2: {p1.Equals p2}"
Public Class Person Private _name As String Public Property Name As String Get Return _name End Get Set _name = value End Set End Property Public Overrides Function Equals(obj As Object) As Boolean ' This implementation contains an error in program logic: ' It assumes that the obj argument is not null. Dim p As Person = CType(obj, Person) Return Me.Name.Equals(p.Name) End Function End Class Module Example2 Public Sub Main() Dim p1 As New Person() p1.Name = "John" Dim p2 As Person = Nothing ' The following throws a NullReferenceException. Console.WriteLine("p1 = p2: {0}", p1.Equals(p2)) End Sub End Module
La excepción NullReferenceException que da como resultado cuando
obj
esnull
se puede eliminar modificando el código fuente para probar explícitamente el valor null antes de llamar a la invalidación Object.Equals y, a continuación, volver a compilar. El ejemplo siguiente contiene el código fuente corregido que controla un argumentonull
.using System; public class Person2 { private string _name; public string Name { get { return _name; } set { _name = value; } } public override int GetHashCode() { return this.Name.GetHashCode(); } public override bool Equals(object obj) { // This implementation handles a null obj argument. Person2 p = obj as Person2; if (p == null) return false; else return this.Name.Equals(p.Name); } } public class UsageErrorsEx2 { public static void Main() { Person2 p1 = new Person2(); p1.Name = "John"; Person2 p2 = null; Console.WriteLine("p1 = p2: {0}", p1.Equals(p2)); } } // The example displays the following output: // p1 = p2: False
// In F#, null is not a valid state for declared types // without 'AllowNullLiteralAttribute' [<AllowNullLiteral>] type Person() = member val Name = "" with get, set override this.GetHashCode() = this.Name.GetHashCode() override this.Equals(obj) = // This implementation handles a null obj argument. match obj with | :? Person as p -> this.Name.Equals p.Name | _ -> false let p1 = Person() p1.Name <- "John" let p2: Person = null printfn $"p1 = p2: {p1.Equals p2}" // The example displays the following output: // p1 = p2: False
Public Class Person2 Private _name As String Public Property Name As String Get Return _name End Get Set _name = Value End Set End Property Public Overrides Function Equals(obj As Object) As Boolean ' This implementation handles a null obj argument. Dim p As Person2 = TryCast(obj, Person2) If p Is Nothing Then Return False Else Return Me.Name.Equals(p.Name) End If End Function End Class Module Example3 Public Sub Main() Dim p1 As New Person2() p1.Name = "John" Dim p2 As Person2 = Nothing Console.WriteLine("p1 = p2: {0}", p1.Equals(p2)) End Sub End Module ' The example displays the following output: ' p1 = p2: False
En lugar de usar el control de excepciones para los errores de uso, puede usar el método Debug.Assert para identificar errores de uso en compilaciones de depuración y el método Trace.Assert para identificar errores de uso en compilaciones de depuración y de lanzamiento. Para obtener más información, vea Aserciones en el código administrado.
Errores del programa. Un error de programa es un error en tiempo de ejecución que no se puede evitar necesariamente escribiendo código sin errores.
En algunos casos, un error de programa puede reflejar una condición de error esperada o rutinaria. En este caso, es posible que quiera evitar el uso del control de excepciones para tratar el error del programa y, en su lugar, reintentar la operación. Por ejemplo, si se espera que el usuario escriba una fecha en un formato determinado, puede analizar la cadena de fecha llamando al método DateTime.TryParseExact, que devuelve un valor Boolean que indica si la operación de análisis se realizó correctamente, en lugar de usar el método DateTime.ParseExact, que inicia una excepción FormatException si la cadena de fecha no se puede convertir en un valor DateTime. Del mismo modo, si un usuario intenta abrir un archivo que no existe, primero puede llamar al método File.Exists para comprobar si el archivo existe y, en caso contrario, preguntar al usuario si desea crearlo.
En otros casos, un error de programa refleja una condición de error inesperada que se puede controlar en el código. Por ejemplo, incluso si ha comprobado que existe un archivo, puede que se elimine antes de que pueda abrirlo, o que esté dañado. En ese caso, al intentar abrir el archivo, crear instancias de un objeto StreamReader o llamar al método Open puede iniciar una excepción FileNotFoundException. En estos casos, debe usar el control de excepciones para recuperarse del error.
Errores del sistema. Un error del sistema es un error en tiempo de ejecución que no se puede controlar mediante programación de forma significativa. Por ejemplo, cualquier método puede iniciar una excepción OutOfMemoryException si Common Language Runtime no puede asignar memoria adicional. Normalmente, los errores del sistema no se controlan mediante el control de excepciones. En su lugar, puede usar un evento como AppDomain.UnhandledException y llamar al método Environment.FailFast para registrar la información de excepción y notificar al usuario el error antes de que finalice la aplicación.
Bloques Try/Catch
Common Language Runtime proporciona un modelo de control de excepciones basado en la representación de excepciones como objetos, y la separación del código de programa y el control de excepciones en bloques try
y catch
. Puede haber uno o varios bloques catch
, cada uno diseñado para controlar un tipo determinado de excepción, o un bloque diseñado para detectar una excepción más específica que otro bloque.
Si una aplicación controla las excepciones que se producen durante la ejecución de un bloque de código de aplicación, el código debe colocarse dentro de una instrucción try
y se denomina bloque try
. El código de aplicación que controla las excepciones iniciadas por un bloque try
se coloca dentro de una instrucción catch
y se denomina bloque catch
. Cero o más bloques catch
están asociados a un bloque try
y cada bloque catch
incluye un filtro de tipo que determina los tipos de excepciones que controla.
Cuando se produce una excepción en un bloque try
, el sistema busca en los bloques catch
asociados en el orden en que aparecen en el código de la aplicación, hasta que encuentra un bloque catch
que controla la excepción. Un bloque catch
controla una excepción de tipo T
si el filtro de tipo del bloque catch especifica T
o cualquier tipo del que T
derive. El sistema deja de buscar después de encontrar el primer bloque catch
que controla la excepción. Por este motivo, en el código de la aplicación, se debe especificar un bloque catch
que controle un tipo antes de un bloque catch
que controle sus tipos base, como se muestra en el ejemplo que sigue a esta sección. Un bloque catch que controla System.Exception
se especifica en último lugar.
Si ninguno de los bloques catch
asociados con el bloque try
actual controla la excepción y el bloque try
actual se anida dentro de otros bloques try
de la llamada actual, se buscan los bloques catch
asociados con el siguiente bloque try
envolvente. Si no se encuentra ningún bloque catch
para la excepción, el sistema busca niveles de anidamiento anteriores en la llamada actual. Si no se encuentra ningún bloque catch
para la excepción en la llamada actual, la excepción se pasa a la pila de llamadas y se busca en el marco de pila anterior un bloque catch
que controle la excepción. La búsqueda de la pila de llamadas continúa hasta que se controla la excepción o hasta que no existen más fotogramas en la pila de llamadas. Si se alcanza la parte superior de la pila de llamadas sin encontrar un bloque catch
que controla la excepción, el controlador de excepciones predeterminado la controla y la aplicación finaliza.
F# try..with (expresión)
F# no usa bloques catch
. En su lugar, una excepción generada coincide con el patrón mediante un único bloque with
. Como se trata de una expresión, en lugar de una instrucción, todas las rutas de acceso deben devolver el mismo tipo. Para obtener más información, consulte try...with (expresión).
Características del tipo de excepción
Los tipos de excepciones admiten las siguientes características:
Texto legible que describe el error. Cuando se produce una excepción, el tiempo de ejecución pone a disposición del usuario un mensaje de texto para informarle de la naturaleza del error y sugerirle una acción para resolver el problema. Este mensaje de texto se mantiene en la propiedad Message del objeto de excepción. Durante la creación del objeto de excepción, puede pasar una cadena de texto al constructor para describir los detalles de esa excepción determinada. Si no se proporciona ningún argumento de mensaje de error al constructor, se usa el mensaje de error predeterminado. Para obtener más información, vea la propiedad Message.
Estado de la pila de llamadas cuando se inició la excepción. La propiedad StackTrace contiene un seguimiento de la pila que se puede usar para determinar dónde se produce el error en el código. El seguimiento de la pila enumera todos los métodos llamados y los números de línea del archivo de origen donde se realizan las llamadas.
Propiedades de la clase de excepción
La clase Exception incluye una serie de propiedades que ayudan a identificar la ubicación del código, el tipo, el archivo de ayuda y el motivo de la excepción: StackTrace, InnerException, Message, HelpLink, HResult, Source, TargetSite y Data.
Cuando existe una relación causal entre dos o más excepciones, la propiedad InnerException mantiene esta información. La excepción externa se inicia en respuesta a esta excepción interna. El código que controla la excepción externa puede usar la información de la excepción interna anterior para controlar el error de forma más adecuada. La información complementaria sobre la excepción se puede almacenar como una colección de pares clave-valor en la propiedad Data.
La cadena de mensaje de error que se pasa al constructor durante la creación del objeto de excepción debe localizarse y se puede proporcionar desde un archivo de recursos mediante la clase ResourceManager. Para obtener más información sobre los recursos localizados, consulte los temas Crear ensamblados satélite y Empaquetar e implementar recursos.
Para proporcionar al usuario información extensa sobre por qué se produjo la excepción, la propiedad HelpLink puede contener una dirección URL (o URN) en un archivo de ayuda.
La clase Exception usa el HRESULT COR_E_EXCEPTION
, que tiene el valor 0x80131500.
Para obtener una lista con los valores de propiedad iniciales de una instancia de la clase Exception, vea los constructores Exception.
Consideraciones de rendimiento
Al iniciar o controlar una excepción, se consume una cantidad significativa de recursos del sistema y tiempo de ejecución. Inicie excepciones solo para controlar condiciones verdaderamente extraordinarias, no para controlar eventos predecibles o el control de flujo. Por ejemplo, en algunos casos, como cuando se desarrolla una biblioteca de clases, es razonable iniciar una excepción si un argumento de método no es válido, ya que se espera que se llame al método con parámetros válidos. Un argumento de método no válido, si no es el resultado de un error de uso, significa que se ha producido algo extraordinario. Por el contrario, no inicie una excepción si la entrada del usuario no es válida, ya que puede esperar que los usuarios escriban ocasionalmente datos no válidos. En su lugar, proporcione un mecanismo de reintento para que los usuarios puedan escribir una entrada válida. Tampoco debe usar excepciones para controlar los errores de uso. En su lugar, use aserciones para identificar y corregir errores de uso.
Además, no inicie una excepción cuando un código de retorno sea suficiente; no convierta un código de retorno en una excepción; y no detecte rutinariamente una excepción, la omita y, a continuación, continúe procesando.
Volver a iniciar una excepción
En muchos casos, un controlador de excepciones simplemente quiere pasar la excepción al autor de la llamada. Esto suele ocurrir en:
Una biblioteca de clases que, a su vez, ajusta las llamadas a métodos de la biblioteca de clases de .NET u otras bibliotecas de clases.
Una aplicación o biblioteca que encuentra una excepción grave. El controlador de excepciones puede registrar la excepción y, a continuación, volver a iniciar la excepción.
La manera recomendada de volver a iniciar una excepción es simplemente usar la instrucción throw en C#, la función reraise en F#, y la instrucción Throw en Visual Basic sin incluir una expresión. Esto garantiza que toda la información de pila de llamadas se conserve cuando la excepción se propague al autor de la llamada. Esto se ilustra en el siguiente ejemplo: Un método de extensión de cadena, FindOccurrences
, ajusta una o varias llamadas a String.IndexOf(String, Int32) sin validar sus argumentos de antemano.
using System;
using System.Collections.Generic;
public static class Library1
{
public static int[] FindOccurrences(this String s, String f)
{
var indexes = new List<int>();
int currentIndex = 0;
try
{
while (currentIndex >= 0 && currentIndex < s.Length)
{
currentIndex = s.IndexOf(f, currentIndex);
if (currentIndex >= 0)
{
indexes.Add(currentIndex);
currentIndex++;
}
}
}
catch (ArgumentNullException)
{
// Perform some action here, such as logging this exception.
throw;
}
return indexes.ToArray();
}
}
open System
module Library =
let findOccurrences (s: string) (f: string) =
let indexes = ResizeArray()
let mutable currentIndex = 0
try
while currentIndex >= 0 && currentIndex < s.Length do
currentIndex <- s.IndexOf(f, currentIndex)
if currentIndex >= 0 then
indexes.Add currentIndex
currentIndex <- currentIndex + 1
with :? ArgumentNullException ->
// Perform some action here, such as logging this exception.
reraise ()
indexes.ToArray()
Imports System.Collections.Generic
Imports System.Runtime.CompilerServices
Public Module Library
<Extension()>
Public Function FindOccurrences1(s As String, f As String) As Integer()
Dim indexes As New List(Of Integer)
Dim currentIndex As Integer = 0
Try
Do While currentIndex >= 0 And currentIndex < s.Length
currentIndex = s.IndexOf(f, currentIndex)
If currentIndex >= 0 Then
indexes.Add(currentIndex)
currentIndex += 1
End If
Loop
Catch e As ArgumentNullException
' Perform some action here, such as logging this exception.
Throw
End Try
Return indexes.ToArray()
End Function
End Module
A continuación, un autor de llamada llama a FindOccurrences
dos veces. En la segunda llamada a FindOccurrences
, el autor de la llamada pasa un null
como cadena de búsqueda, lo que hace que el método String.IndexOf(String, Int32) inicie una excepción ArgumentNullException. El método FindOccurrences
controla esta excepción y se pasa al autor de la llamada. Dado que la instrucción throw se usa sin expresión, la salida del ejemplo muestra que se conserva la pila de llamadas.
public class RethrowEx1
{
public static void Main()
{
String s = "It was a cold day when...";
int[] indexes = s.FindOccurrences("a");
ShowOccurrences(s, "a", indexes);
Console.WriteLine();
String toFind = null;
try
{
indexes = s.FindOccurrences(toFind);
ShowOccurrences(s, toFind, indexes);
}
catch (ArgumentNullException e)
{
Console.WriteLine("An exception ({0}) occurred.",
e.GetType().Name);
Console.WriteLine("Message:\n {0}\n", e.Message);
Console.WriteLine("Stack Trace:\n {0}\n", e.StackTrace);
}
}
private static void ShowOccurrences(String s, String toFind, int[] indexes)
{
Console.Write("'{0}' occurs at the following character positions: ",
toFind);
for (int ctr = 0; ctr < indexes.Length; ctr++)
Console.Write("{0}{1}", indexes[ctr],
ctr == indexes.Length - 1 ? "" : ", ");
Console.WriteLine();
}
}
// The example displays the following output:
// 'a' occurs at the following character positions: 4, 7, 15
//
// An exception (ArgumentNullException) occurred.
// Message:
// Value cannot be null.
// Parameter name: value
//
// Stack Trace:
// at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
// ngComparison comparisonType)
// at Library.FindOccurrences(String s, String f)
// at Example.Main()
open Library
let showOccurrences toFind (indexes: int[]) =
printf $"'{toFind}' occurs at the following character positions: "
for i = 0 to indexes.Length - 1 do
printf $"""{indexes[i]}{if i = indexes.Length - 1 then "" else ", "}"""
printfn ""
let s = "It was a cold day when..."
let indexes = findOccurrences s "a"
showOccurrences "a" indexes
printfn ""
let toFind: string = null
try
let indexes = findOccurrences s toFind
showOccurrences toFind indexes
with :? ArgumentNullException as e ->
printfn $"An exception ({e.GetType().Name}) occurred."
printfn $"Message:\n {e.Message}\n"
printfn $"Stack Trace:\n {e.StackTrace}\n"
// The example displays the following output:
// 'a' occurs at the following character positions: 4, 7, 15
//
// An exception (ArgumentNullException) occurred.
// Message:
// Value cannot be null. (Parameter 'value')
//
// Stack Trace:
// at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
// ngComparison comparisonType)
// at Library.findOccurrences(String s, String f)
// at <StartupCode$fs>.main@()
Module Example1
Public Sub Main()
Dim s As String = "It was a cold day when..."
Dim indexes() As Integer = s.FindOccurrences1("a")
ShowOccurrences(s, "a", indexes)
Console.WriteLine()
Dim toFind As String = Nothing
Try
indexes = s.FindOccurrences1(toFind)
ShowOccurrences(s, toFind, indexes)
Catch e As ArgumentNullException
Console.WriteLine("An exception ({0}) occurred.",
e.GetType().Name)
Console.WriteLine("Message:{0} {1}{0}", vbCrLf, e.Message)
Console.WriteLine("Stack Trace:{0} {1}{0}", vbCrLf, e.StackTrace)
End Try
End Sub
Private Sub ShowOccurrences(s As String, toFind As String, indexes As Integer())
Console.Write("'{0}' occurs at the following character positions: ",
toFind)
For ctr As Integer = 0 To indexes.Length - 1
Console.Write("{0}{1}", indexes(ctr),
If(ctr = indexes.Length - 1, "", ", "))
Next
Console.WriteLine()
End Sub
End Module
' The example displays the following output:
' 'a' occurs at the following character positions: 4, 7, 15
'
' An exception (ArgumentNullException) occurred.
' Message:
' Value cannot be null.
' Parameter name: value
'
' Stack Trace:
' at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
' ngComparison comparisonType)
' at Library.FindOccurrences(String s, String f)
' at Example.Main()
En cambio, si se vuelve a iniciar la excepción mediante esta instrucción:
throw e;
Throw e
raise e
... a continuación, no se conserva la pila de llamadas completa y el ejemplo generaría el siguiente resultado:
'a' occurs at the following character positions: 4, 7, 15
An exception (ArgumentNullException) occurred.
Message:
Value cannot be null.
Parameter name: value
Stack Trace:
at Library.FindOccurrences(String s, String f)
at Example.Main()
Una alternativa un poco más complicada es iniciar una nueva excepción y conservar la información de pila de llamadas de la excepción original en una excepción interna. A continuación, el autor de la llamada puede usar la propiedad InnerException de la nueva excepción para recuperar el marco de pila y otra información sobre la excepción original. En este caso, la instrucción throw es:
throw new ArgumentNullException("You must supply a search string.", e);
raise (ArgumentNullException("You must supply a search string.", e) )
Throw New ArgumentNullException("You must supply a search string.",
e)
El código de usuario que controla la excepción debe saber que la propiedad InnerException contiene información sobre la excepción original, como se muestra en el siguiente controlador de excepciones.
try
{
indexes = s.FindOccurrences(toFind);
ShowOccurrences(s, toFind, indexes);
}
catch (ArgumentNullException e)
{
Console.WriteLine("An exception ({0}) occurred.",
e.GetType().Name);
Console.WriteLine(" Message:\n{0}", e.Message);
Console.WriteLine(" Stack Trace:\n {0}", e.StackTrace);
Exception ie = e.InnerException;
if (ie != null)
{
Console.WriteLine(" The Inner Exception:");
Console.WriteLine(" Exception Name: {0}", ie.GetType().Name);
Console.WriteLine(" Message: {0}\n", ie.Message);
Console.WriteLine(" Stack Trace:\n {0}\n", ie.StackTrace);
}
}
// The example displays the following output:
// 'a' occurs at the following character positions: 4, 7, 15
//
// An exception (ArgumentNullException) occurred.
// Message: You must supply a search string.
//
// Stack Trace:
// at Library.FindOccurrences(String s, String f)
// at Example.Main()
//
// The Inner Exception:
// Exception Name: ArgumentNullException
// Message: Value cannot be null.
// Parameter name: value
//
// Stack Trace:
// at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
// ngComparison comparisonType)
// at Library.FindOccurrences(String s, String f)
try
let indexes = findOccurrences s toFind
showOccurrences toFind indexes
with :? ArgumentNullException as e ->
printfn $"An exception ({e.GetType().Name}) occurred."
printfn $" Message:\n{e.Message}"
printfn $" Stack Trace:\n {e.StackTrace}"
let ie = e.InnerException
if ie <> null then
printfn " The Inner Exception:"
printfn $" Exception Name: {ie.GetType().Name}"
printfn $" Message: {ie.Message}\n"
printfn $" Stack Trace:\n {ie.StackTrace}\n"
// The example displays the following output:
// 'a' occurs at the following character positions: 4, 7, 15
//
// An exception (ArgumentNullException) occurred.
// Message: You must supply a search string.
//
// Stack Trace:
// at Library.FindOccurrences(String s, String f)
// at Example.Main()
//
// The Inner Exception:
// Exception Name: ArgumentNullException
// Message: Value cannot be null.
// Parameter name: value
//
// Stack Trace:
// at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
// ngComparison comparisonType)
// at Library.FindOccurrences(String s, String f)
Try
indexes = s.FindOccurrences(toFind)
ShowOccurrences(s, toFind, indexes)
Catch e As ArgumentNullException
Console.WriteLine("An exception ({0}) occurred.",
e.GetType().Name)
Console.WriteLine(" Message: {1}{0}", vbCrLf, e.Message)
Console.WriteLine(" Stack Trace:{0} {1}{0}", vbCrLf, e.StackTrace)
Dim ie As Exception = e.InnerException
If ie IsNot Nothing Then
Console.WriteLine(" The Inner Exception:")
Console.WriteLine(" Exception Name: {0}", ie.GetType().Name)
Console.WriteLine(" Message: {1}{0}", vbCrLf, ie.Message)
Console.WriteLine(" Stack Trace:{0} {1}{0}", vbCrLf, ie.StackTrace)
End If
End Try
' The example displays the following output:
' 'a' occurs at the following character positions: 4, 7, 15
'
' An exception (ArgumentNullException) occurred.
' Message: You must supply a search string.
'
' Stack Trace:
' at Library.FindOccurrences(String s, String f)
' at Example.Main()
'
' The Inner Exception:
' Exception Name: ArgumentNullException
' Message: Value cannot be null.
' Parameter name: value
'
' Stack Trace:
' at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
' ngComparison comparisonType)
' at Library.FindOccurrences(String s, String f)
Elegir excepciones estándar
Cuando tenga que iniciar una excepción, a menudo puede usar un tipo de excepción existente en .NET en lugar de implementar una excepción personalizada. Debe usar un tipo de excepción estándar en estas dos condiciones:
Está iniciando una excepción causada por un error de uso (es decir, por un error en la lógica del programa realizada por el desarrollador que llama al método). Normalmente, se iniciaría una excepción como ArgumentException, ArgumentNullException, InvalidOperationException o NotSupportedException. La cadena que se proporciona al constructor del objeto de excepción al crear una instancia del objeto de excepción debe describir el error para que el desarrollador pueda corregirlo. Para obtener más información, vea la propiedad Message.
Está controlando un error que se puede comunicar al autor de la llamada con una excepción de .NET existente. Debe iniciar la excepción más derivada posible. Por ejemplo, si un método requiere que un argumento sea un miembro válido de un tipo de enumeración, debe iniciar un InvalidEnumArgumentException (la clase más derivada) en lugar de un ArgumentException.
En la tabla siguiente se enumeran los tipos de excepción comunes y las condiciones en las que se iniciarían.
Exception | Condición |
---|---|
ArgumentException | Un argumento que no es NULL que se pasa a un método no es válido. |
ArgumentNullException | Un argumento que se pasa a un método es null . |
ArgumentOutOfRangeException | Un argumento está fuera del intervalo de valores válidos. |
DirectoryNotFoundException | Parte de una ruta de acceso de directorio no es válida. |
DivideByZeroException | El denominador de una operación de número entero o división Decimal es cero. |
DriveNotFoundException | Un controlador no está disponible o no existe. |
FileNotFoundException | No existe un archivo. |
FormatException | Un valor no tiene un formato adecuado para convertirse a partir de una cadena mediante un método de conversión como Parse . |
IndexOutOfRangeException | Un índice está fuera de los límites de una matriz o colección. |
InvalidOperationException | Una llamada de método no es válida en el estado actual de un objeto. |
KeyNotFoundException | No se encuentra la clave especificada para acceder a un miembro de una colección. |
NotImplementedException | No se implementa un método o una operación. |
NotSupportedException | No se admite un método o una operación. |
ObjectDisposedException | Se realiza una operación en un objeto que se ha eliminado. |
OverflowException | Una operación aritmética, de coerción o de conversión da como resultado un desbordamiento. |
PathTooLongException | Una ruta de acceso o nombre de archivo supera la longitud máxima definida por el sistema. |
PlatformNotSupportedException | La operación no es compatible con la plataforma actual. |
RankException | Una matriz con el número incorrecto de dimensiones se pasa a un método. |
TimeoutException | El intervalo de tiempo asignado a una operación ha expirado. |
UriFormatException | Se usa un identificador uniforme de recursos (URI) no válido. |
Implementar excepciones personalizadas
En los casos siguientes, el uso de una excepción .NET existente para controlar una condición de error no es adecuado:
Cuando la excepción refleja un error de programa único que no se puede asignar a una excepción .NET existente.
Cuando la excepción requiere un control diferente del que es apropiado para una excepción .NET existente, o la excepción debe distinguirse de una excepción similar. Por ejemplo, si inicia una excepción ArgumentOutOfRangeException al analizar la representación numérica de una cadena que está fuera del intervalo del tipo entero de destino, no querrá usar la misma excepción para un error que resulte de que el autor de la llamada no proporcione los valores restringidos apropiados al llamar al método.
La clase Exception es la clase base de todas las excepciones de .NET. Muchas clases derivadas dependen del comportamiento heredado de los miembros de la clase Exception; no invalidan los miembros de Exception, ni definen ningún miembro único.
Para definir su propia clase de excepción:
Defina una clase que herede de Exception. Si es necesario, defina los miembros únicos que necesite la clase para proporcionar información adicional sobre la excepción. Por ejemplo, la clase ArgumentException incluye una propiedad ParamName que especifica el nombre del parámetro cuyo argumento provocó la excepción, y la propiedad RegexMatchTimeoutException incluye una propiedad MatchTimeout que indica el intervalo de tiempo de espera.
Si es necesario, invalide los miembros heredados cuya funcionalidad desea cambiar o modificar. Tenga en cuenta que la mayoría de las clases derivadas existentes de Exception no invalidan el comportamiento de los miembros heredados.
Determine si el objeto de excepción personalizado es serializable. La serialización permite guardar información sobre la excepción y permite que un servidor y un proxy cliente compartan información de excepción en un contexto de comunicación remota. Para que el objeto de excepción sea serializable, márquelo con el atributo SerializableAttribute.
Defina los constructores de la clase de excepción. Normalmente, las clases de excepción tienen uno o varios de los constructores siguientes:
Exception(), que usa valores predeterminados para inicializar las propiedades de un nuevo objeto de excepción.
Exception(String), que inicializa un nuevo objeto de excepción con un mensaje de error especificado.
Exception(String, Exception), que inicializa un nuevo objeto de excepción con un mensaje de error especificado y una excepción interna.
Exception(SerializationInfo, StreamingContext), que es un constructor
protected
que inicializa un nuevo objeto de excepción a partir de datos serializados. Debe implementar este constructor si ha elegido hacer serializable el objeto de excepción.
En el ejemplo siguiente se muestra el uso de una clase de excepción personalizada. Define una excepción NotPrimeException
que se inicia cuando un cliente intenta recuperar una secuencia de números primos especificando un número inicial que no es primo. La excepción define una nueva propiedad, NonPrime
, que devuelve el número no primo que provocó la excepción. Además de implementar un constructor sin parámetros protegido y un constructor con parámetros SerializationInfo y StreamingContext para la serialización, la clase NotPrimeException
define tres constructores adicionales para admitir la propiedad NonPrime
. Cada constructor llama a un constructor de clase base además de conservar el valor del número no primo. La clase NotPrimeException
también se marca con el atributo SerializableAttribute.
using System;
using System.Runtime.Serialization;
[Serializable()]
public class NotPrimeException : Exception
{
private int notAPrime;
protected NotPrimeException()
: base()
{ }
public NotPrimeException(int value) :
base(String.Format("{0} is not a prime number.", value))
{
notAPrime = value;
}
public NotPrimeException(int value, string message)
: base(message)
{
notAPrime = value;
}
public NotPrimeException(int value, string message, Exception innerException) :
base(message, innerException)
{
notAPrime = value;
}
protected NotPrimeException(SerializationInfo info,
StreamingContext context)
: base(info, context)
{ }
public int NonPrime
{ get { return notAPrime; } }
}
namespace global
open System
open System.Runtime.Serialization
[<Serializable>]
type NotPrimeException =
inherit Exception
val notAPrime: int
member this.NonPrime =
this.notAPrime
new (value) =
{ inherit Exception($"%i{value} is not a prime number."); notAPrime = value }
new (value, message) =
{ inherit Exception(message); notAPrime = value }
new (value, message, innerException: Exception) =
{ inherit Exception(message, innerException); notAPrime = value }
// F# does not support protected members
new () =
{ inherit Exception(); notAPrime = 0 }
new (info: SerializationInfo, context: StreamingContext) =
{ inherit Exception(info, context); notAPrime = 0 }
Imports System.Runtime.Serialization
<Serializable()> _
Public Class NotPrimeException : Inherits Exception
Private notAPrime As Integer
Protected Sub New()
MyBase.New()
End Sub
Public Sub New(value As Integer)
MyBase.New(String.Format("{0} is not a prime number.", value))
notAPrime = value
End Sub
Public Sub New(value As Integer, message As String)
MyBase.New(message)
notAPrime = value
End Sub
Public Sub New(value As Integer, message As String, innerException As Exception)
MyBase.New(message, innerException)
notAPrime = value
End Sub
Protected Sub New(info As SerializationInfo,
context As StreamingContext)
MyBase.New(info, context)
End Sub
Public ReadOnly Property NonPrime As Integer
Get
Return notAPrime
End Get
End Property
End Class
La clase PrimeNumberGenerator
que se muestra en el ejemplo siguiente usa la criba de Eratóstenes para calcular la secuencia de números primos desde 2 hasta un límite especificado por el cliente en la llamada a su constructor de clase. El método GetPrimesFrom
devuelve todos los números primos que son mayores o iguales a un límite inferior especificado, pero produce un NotPrimeException
si ese límite inferior no es un número primo.
using System;
using System.Collections.Generic;
[Serializable]
public class PrimeNumberGenerator
{
private const int START = 2;
private int maxUpperBound = 10000000;
private int upperBound;
private bool[] primeTable;
private List<int> primes = new List<int>();
public PrimeNumberGenerator(int upperBound)
{
if (upperBound > maxUpperBound)
{
string message = String.Format(
"{0} exceeds the maximum upper bound of {1}.",
upperBound, maxUpperBound);
throw new ArgumentOutOfRangeException(message);
}
this.upperBound = upperBound;
// Create array and mark 0, 1 as not prime (True).
primeTable = new bool[upperBound + 1];
primeTable[0] = true;
primeTable[1] = true;
// Use Sieve of Eratosthenes to determine prime numbers.
for (int ctr = START; ctr <= (int)Math.Ceiling(Math.Sqrt(upperBound));
ctr++)
{
if (primeTable[ctr]) continue;
for (int multiplier = ctr; multiplier <= upperBound / ctr; multiplier++)
if (ctr * multiplier <= upperBound) primeTable[ctr * multiplier] = true;
}
// Populate array with prime number information.
int index = START;
while (index != -1)
{
index = Array.FindIndex(primeTable, index, (flag) => !flag);
if (index >= 1)
{
primes.Add(index);
index++;
}
}
}
public int[] GetAllPrimes()
{
return primes.ToArray();
}
public int[] GetPrimesFrom(int prime)
{
int start = primes.FindIndex((value) => value == prime);
if (start < 0)
throw new NotPrimeException(prime, String.Format("{0} is not a prime number.", prime));
else
return primes.FindAll((value) => value >= prime).ToArray();
}
}
namespace global
open System
[<Serializable>]
type PrimeNumberGenerator(upperBound) =
let start = 2
let maxUpperBound = 10000000
let primes = ResizeArray()
let primeTable =
upperBound + 1
|> Array.zeroCreate<bool>
do
if upperBound > maxUpperBound then
let message = $"{upperBound} exceeds the maximum upper bound of {maxUpperBound}."
raise (ArgumentOutOfRangeException message)
// Create array and mark 0, 1 as not prime (True).
primeTable[0] <- true
primeTable[1] <- true
// Use Sieve of Eratosthenes to determine prime numbers.
for i = start to float upperBound |> sqrt |> ceil |> int do
if not primeTable[i] then
for multiplier = i to upperBound / i do
if i * multiplier <= upperBound then
primeTable[i * multiplier] <- true
// Populate array with prime number information.
let mutable index = start
while index <> -1 do
index <- Array.FindIndex(primeTable, index, fun flag -> not flag)
if index >= 1 then
primes.Add index
index <- index + 1
member _.GetAllPrimes() =
primes.ToArray()
member _.GetPrimesFrom(prime) =
let start =
Seq.findIndex ((=) prime) primes
if start < 0 then
raise (NotPrimeException(prime, $"{prime} is not a prime number.") )
else
Seq.filter ((>=) prime) primes
|> Seq.toArray
Imports System.Collections.Generic
<Serializable()> Public Class PrimeNumberGenerator
Private Const START As Integer = 2
Private maxUpperBound As Integer = 10000000
Private upperBound As Integer
Private primeTable() As Boolean
Private primes As New List(Of Integer)
Public Sub New(upperBound As Integer)
If upperBound > maxUpperBound Then
Dim message As String = String.Format(
"{0} exceeds the maximum upper bound of {1}.",
upperBound, maxUpperBound)
Throw New ArgumentOutOfRangeException(message)
End If
Me.upperBound = upperBound
' Create array and mark 0, 1 as not prime (True).
ReDim primeTable(upperBound)
primeTable(0) = True
primeTable(1) = True
' Use Sieve of Eratosthenes to determine prime numbers.
For ctr As Integer = START To CInt(Math.Ceiling(Math.Sqrt(upperBound)))
If primeTable(ctr) Then Continue For
For multiplier As Integer = ctr To CInt(upperBound \ ctr)
If ctr * multiplier <= upperBound Then primeTable(ctr * multiplier) = True
Next
Next
' Populate array with prime number information.
Dim index As Integer = START
Do While index <> -1
index = Array.FindIndex(primeTable, index, Function(flag)
Return Not flag
End Function)
If index >= 1 Then
primes.Add(index)
index += 1
End If
Loop
End Sub
Public Function GetAllPrimes() As Integer()
Return primes.ToArray()
End Function
Public Function GetPrimesFrom(prime As Integer) As Integer()
Dim start As Integer = primes.FindIndex(Function(value)
Return value = prime
End Function)
If start < 0 Then
Throw New NotPrimeException(prime, String.Format("{0} is not a prime number.", prime))
Else
Return primes.FindAll(Function(value)
Return value >= prime
End Function).ToArray()
End If
End Function
End Class
En el ejemplo siguiente se realizan dos llamadas al método GetPrimesFrom
con números no primos, uno de los cuales cruza los límites del dominio de aplicación. En ambos casos, la excepción se inicia y se controla correctamente en el código de cliente.
using System;
using System.Reflection;
class Example1
{
public static void Main()
{
int limit = 10000000;
PrimeNumberGenerator primes = new PrimeNumberGenerator(limit);
int start = 1000001;
try
{
int[] values = primes.GetPrimesFrom(start);
Console.WriteLine("There are {0} prime numbers from {1} to {2}",
start, limit);
}
catch (NotPrimeException e)
{
Console.WriteLine("{0} is not prime", e.NonPrime);
Console.WriteLine(e);
Console.WriteLine("--------");
}
AppDomain domain = AppDomain.CreateDomain("Domain2");
PrimeNumberGenerator gen = (PrimeNumberGenerator)domain.CreateInstanceAndUnwrap(
typeof(Example).Assembly.FullName,
"PrimeNumberGenerator", true,
BindingFlags.Default, null,
new object[] { 1000000 }, null, null);
try
{
start = 100;
Console.WriteLine(gen.GetPrimesFrom(start));
}
catch (NotPrimeException e)
{
Console.WriteLine("{0} is not prime", e.NonPrime);
Console.WriteLine(e);
Console.WriteLine("--------");
}
}
}
open System
open System.Reflection
let limit = 10000000
let primes = PrimeNumberGenerator limit
let start = 1000001
try
let values = primes.GetPrimesFrom start
printfn $"There are {values.Length} prime numbers from {start} to {limit}"
with :? NotPrimeException as e ->
printfn $"{e.NonPrime} is not prime"
printfn $"{e}"
printfn "--------"
let domain = AppDomain.CreateDomain "Domain2"
let gen =
domain.CreateInstanceAndUnwrap(
typeof<PrimeNumberGenerator>.Assembly.FullName,
"PrimeNumberGenerator", true,
BindingFlags.Default, null,
[| box 1000000 |], null, null)
:?> PrimeNumberGenerator
try
let start = 100
printfn $"{gen.GetPrimesFrom start}"
with :? NotPrimeException as e ->
printfn $"{e.NonPrime} is not prime"
printfn $"{e}"
printfn "--------"
Imports System.Reflection
Module Example
Sub Main()
Dim limit As Integer = 10000000
Dim primes As New PrimeNumberGenerator(limit)
Dim start As Integer = 1000001
Try
Dim values() As Integer = primes.GetPrimesFrom(start)
Console.WriteLine("There are {0} prime numbers from {1} to {2}",
start, limit)
Catch e As NotPrimeException
Console.WriteLine("{0} is not prime", e.NonPrime)
Console.WriteLine(e)
Console.WriteLine("--------")
End Try
Dim domain As AppDomain = AppDomain.CreateDomain("Domain2")
Dim gen As PrimeNumberGenerator = domain.CreateInstanceAndUnwrap(
GetType(Example).Assembly.FullName,
"PrimeNumberGenerator", True,
BindingFlags.Default, Nothing,
{1000000}, Nothing, Nothing)
Try
start = 100
Console.WriteLine(gen.GetPrimesFrom(start))
Catch e As NotPrimeException
Console.WriteLine("{0} is not prime", e.NonPrime)
Console.WriteLine(e)
Console.WriteLine("--------")
End Try
End Sub
End Module
' The example displays the following output:
' 1000001 is not prime
' NotPrimeException: 1000001 is not a prime number.
' at PrimeNumberGenerator.GetPrimesFrom(Int32 prime)
' at Example.Main()
' --------
' 100 is not prime
' NotPrimeException: 100 is not a prime number.
' at PrimeNumberGenerator.GetPrimesFrom(Int32 prime)
' at Example.Main()
' --------
Ejemplos
En el ejemplo siguiente se muestra un bloque catch
(with
en F#) que se define para controlar los errores ArithmeticException. Este bloque catch
también detecta errores DivideByZeroException, ya que DivideByZeroException deriva de ArithmeticException y no hay ningún bloque catch
definido explícitamente para errores DivideByZeroException.
using System;
class ExceptionTestClass
{
public static void Main()
{
int x = 0;
try
{
int y = 100 / x;
}
catch (ArithmeticException e)
{
Console.WriteLine($"ArithmeticException Handler: {e}");
}
catch (Exception e)
{
Console.WriteLine($"Generic Exception Handler: {e}");
}
}
}
/*
This code example produces the following results:
ArithmeticException Handler: System.DivideByZeroException: Attempted to divide by zero.
at ExceptionTestClass.Main()
*/
module ExceptionTestModule
open System
let x = 0
try
let y = 100 / x
()
with
| :? ArithmeticException as e ->
printfn $"ArithmeticException Handler: {e}"
| e ->
printfn $"Generic Exception Handler: {e}"
// This code example produces the following results:
// ArithmeticException Handler: System.DivideByZeroException: Attempted to divide by zero.
// at <StartupCode$fs>.$ExceptionTestModule.main@()
Class ExceptionTestClass
Public Shared Sub Main()
Dim x As Integer = 0
Try
Dim y As Integer = 100 / x
Catch e As ArithmeticException
Console.WriteLine("ArithmeticException Handler: {0}", e.ToString())
Catch e As Exception
Console.WriteLine("Generic Exception Handler: {0}", e.ToString())
End Try
End Sub
End Class
'
'This code example produces the following results:
'
'ArithmeticException Handler: System.OverflowException: Arithmetic operation resulted in an overflow.
' at ExceptionTestClass.Main()
'