Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Nota
Los grupos de interés de la comunidad ahora se han trasladado de Yammer a Microsoft Viva Engage. Para unirse a una comunidad de Viva Engage y participar en las últimas discusiones, rellene el formulario Solicitar acceso a la comunidad de Viva Engage de Finance and Operations y elija la comunidad a la que desea unirse.
En este artículo se describe el control de excepciones en X++. Maneja los errores usando el lanzamiento, prueba... capturar, finalmente y reintentar sentencias para generar y gestionar excepciones.
Una excepción es un salto regulado que se aleja de la secuencia de ejecución del programa. Los try...catch bloques y el tipo de excepción que se lanza determinan la instrucción en la que se reanuda la ejecución del programa. Una excepción se representa mediante un valor de la enumeración Exception o una instancia de . NET o una clase derivada System.Exception de ella. Una excepción que sueles lanzar es el valor enum Exception:: error. Una práctica común es escribir información diagnóstica en el Infolog antes de lanzar la excepción.
El método Global::error suele ser la mejor manera de escribir información de diagnóstico en el Infolog. Por ejemplo, el método puede recibir un valor de parámetro de entrada que no es válido. En este caso, el método puede producir una excepción para transferir inmediatamente el control a un bloque de código catch que contiene lógica para controlar esta situación de error. No es necesario que sepas la ubicación del bloque de captura que recibe el control cuando se lanza la excepción.
throw: declaraciones
Usa la palabra clave throw para lanzar un valor de enum de Exception . Por ejemplo, la siguiente instrucción produce una excepción de error.
throw Exception::error;
En lugar de lanzar un valor enum, usa la salida del método Global::error como operando para el lanzamiento.
throw Global::error('The parameter value is invalid.');
El método Global::error puede convertir automáticamente una etiqueta en el texto correspondiente. Esta funcionalidad le ayuda a escribir código que se puede localizar más fácilmente.
throw Global::error("@SYS98765");
Puedes llamar a los métodos estáticos en la clase Global sin el prefijo Global:: . Por ejemplo, puedes llamar al método Global::error así.
error('My message.');
Utiliza la palabra clave throw para lanzar excepciones .NET así como los valores del tipo de excepción enumerado.
throw new System.InvalidOperationException("This function is not allowed");
Usa la palabra clave throw sola dentro de un bloque de captura. En este caso, el lanzamiento se comporta como la sentencia rethrow en C#. La excepción original, el mensaje de excepción y su contexto, como la pila de llamadas, se vuelven a lanzar y están disponibles para cualquier sentencia de captura en el código de llamada.
try
{
throw Exception::error;
}
catch
{
// locally handle exception
// then rethrow for caller
throw;
}
Instrucciones try, catch, finally y retry
Cuando ocurre una excepción, el sistema primero la procesa a través de la lista de captura del bloque de intento más interno. Si el sistema encuentra un bloque de captura que gestiona el tipo de excepción que ocurrió, el control del programa salta a ese bloque de captura . Si la lista de captura no tiene ningún bloque que especifique la excepción, el sistema pasa la excepción a la lista de captura del siguiente bloque try más interno. El sistema procesa las sentencias catch en la misma secuencia en la que aparecen en el código.
Es una práctica común que la primera instrucción catch controle el valor de enumeración Exception::Error . Una estrategia es hacer que la última instrucción catch deje el tipo de excepción sin especificar. En este caso, la última instrucción catch controla todas las excepciones que no se controlan mediante ninguna instrucción catch anterior. Esta estrategia es apropiada para el intento más externo... bloques de captura .
Puedes incluir una cláusula opcional de final en try... Capturar declaraciones. La semántica de una cláusula finally es la misma que en C#. El sistema ejecuta las sentencias de la cláusula finally cuando control sale del bloque try , ya sea normalmente o a través de una excepción.
La instrucción retry solo puede aparecer en un bloque catch . La instrucción retry hace que el control salte a la primera línea de código del bloque try asociado. Utiliza la instrucción retry cuando la causa de la excepción pueda ser fijada por el código en el bloque de captura . La instrucción retry le da al código del bloque try otra oportunidad de tener éxito. La instrucción retry borra todos los mensajes que se han escrito en el Infolog desde que el control de programa entró en el bloque try .
Nota
Debe asegurarse de que las instrucciones de reintento no provoquen un bucle infinito. Como práctica recomendada, el bloque try debe incluir una variable que pueda probar para averiguar si está en un bucle.
try
{
// Code here.
}
catch (Exception::Numeric)
{
info("Caught a Numeric exception.");
}
catch
{
info("Caught an exception.");
}
finally
{
// Executed no matter how the try block exits.
}
El controlador de excepciones del sistema
Si ninguna sentencia catch gestiona la excepción, el gestor de excepciones del sistema la gestiona. El controlador de excepciones del sistema no escribe en el Infolog. Por lo tanto, las excepciones no controladas pueden ser difíciles de diagnosticar. Sigue todas estas directrices para proporcionar una gestión eficaz de las excepciones:
- Tenga un bloque try que contenga todas sus declaraciones en el marco más externo de la pila de llamadas.
- Tenga un bloque de captura no calificado al final de su lista de captura más externa.
- Evite iniciar un valor de enumeración Exception directamente.
- Lanza el valor de enum que uno de los siguientes métodos en la clase Global devuelve: Global::error, Global::warning o Global::info. (Puede omitir el prefijo Global:: implícito).
- Cuando detectes una excepción que el Infolog no muestre, llama a la función Global::info para mostrarla.
Exception::CLRError, Exception::UpdateConflictNotRecovered y las excepciones del núcleo del sistema son ejemplos de excepciones que el Infolog no muestra automáticamente.
Excepciones e interoperabilidad de CLR
Puedes llamar a clases y métodos de Microsoft .NET Framework que residen en ensambladores que gestiona el Common Language runtime (CLR). Cuando tu código lanza una instancia de .NET Framework System.Exception , puedes detectarla declarando una variable de tipo System.Exception para capturar cualquier excepción .NET, o puedes capturar un tipo específico de excepción .NET usando una de sus clases derivadas, como se muestra en el siguiente ejemplo.
System.ArgumentException ex;
try
{
throw new System.ArgumentException("Invalid argument specified");
}
catch(ex) // Will catch the System.ArgumentException, given the type of ex.
{
error(ex.Message);
}
Puedes detectar excepciones .NET consultando Exception::CLRError. Tu código puede obtener una referencia a la instancia System.Exception llamando al método CLRInterop::getLastException .
try
{
// call to .NET code which throws exception
}
catch(Exception::CLRError)
{
System.Exception ex = CLRInterop::getLastException();
error(ex.Message);
}
Asegurarse de que se muestran las excepciones
El Infolog no muestra excepciones del tipo Exception::CLREror , porque estas excepciones no se generan mediante una llamada a un método como Global::error. En el bloque catch , el código puede llamar a Global::error para notificar la excepción específica.
Métodos de clase global
En esta sección se describen algunos métodos de clase Global con más detalle. Estos métodos de clase incluyen Global::error, Global::info y Global::exceptionTextFallThrough.
Método Global::error
En el código siguiente se muestra cómo se declara el método de error .
static Exception error
(SysInfoLogStr txt,
URL helpURL = '',
SysInfoAction _sysInfoAction = null)
El tipo de valor devuelto es el valor de enumeración Exception::Error . El método error no produce una excepción. Simplemente proporciona un valor enum que puedes usar en una instrucción throw . La instrucción throw produce la excepción. A continuación se describen los parámetros del método de error . Solo se requiere el primer parámetro.
- SysInfoLogStr txt es un str del texto del mensaje. También puede ser una referencia de etiqueta, como strFmt("@SYS12345", strThingName).
- La dirección URL helpUrl es una referencia a la ubicación de un artículo de Ayuda en el Explorador de aplicaciones, como "KernDoc:\\\\Functions\\substr". El valor del parámetro se ignora si se proporciona _sysInfoAction.
- SysInfoAction es una instancia de una clase que extiende la clase SysInfoAction. Las invalidaciones de método que se recomiendan para la clase secundaria son el método description , el método run , el método pack y el método unpack .
Método Global::info
Utiliza el método Global::info para mostrar el texto en el Infolog. En los programas, escríbelo como info("Mi mensaje.");. Aunque el método info devuelve un valor enum Exception::Info , rara vez quieres lanzar Exception::Info, porque no ocurrió nada inesperado.
Método Global::exceptionTextFallThrough
Ocasionalmente, no quieres hacer nada dentro de tu bloque de captura . Sin embargo, el compilador de X++ genera una advertencia si tiene un bloque catch vacío. Para evitar esta advertencia, llame al método Global::exceptionTextFallThrough en el bloque catch . El método no hace nada, pero satisface al compilador y establece explícitamente la intención.
Excepciones dentro de las transacciones
Si se produce una excepción dentro de una transacción, la transacción se cancela automáticamente (es decir, se produce una operación ttsAbort ). Este comportamiento se aplica tanto a las excepciones que se producen manualmente como a las excepciones que produce el sistema. Cuando se produce una excepción dentro de un bloque de transacciones ttsBegin-ttsCommit , ninguna instrucción catch dentro de ese bloque de transacciones puede procesar la excepción (a menos que sea un UpdateConflict o un DuplicateKeyException). En su lugar, las instrucciones catch más internas que están fuera del bloque de transacciones son las primeras instrucciones catch que se prueban.
Para detectar UpdateConflict o DuplicateKeyException dentro de una transacción, especifique explícitamente la excepción en la sentencia catch así, catch (Exception::DuplicateKeyException). Una instrucción catch{} no puede detectar UpdateConflict o DuplicateKeyException dentro de una transacción.
La cláusula finally se ejecuta incluso en el ámbito de la transacción.
Excepciones y using declaraciones
El alcance de las excepciones no afecta la semântica de using las afirmaciones. La using declaración:
using (var athing = new SomethingDisposable())
{
// Do work.
}
Es semánticamente idéntico a:
var athing = new SomethingDisposable();
try
{
// Do work.
}
finally
{
if (athing != null)
athing.Dispose();
}
Ejemplos de control de excepciones
Mostrar excepciones en el Infolog
En el ejemplo de código siguiente se muestran las excepciones en el registro de información.
// This example shows that a direct throw of Exception::Error does not
// display a message in the Infolog. This is why we recommend the
// Global::error method.
static void TryCatchThrowError1Job(Args _args)
{
/***
The 'throw' does not directly add a message to the Infolog.
The exception is caught.
***/
try
{
info("In the 'try' block. (j1)");
throw Exception::Error;
}
catch (Exception::Error)
{
info("Caught 'Exception::Error'.");
}
/********** Actual Infolog output
Message (03:43:45 pm)
In the 'try' block. (j1)
Caught 'Exception::Error'.
**********/
}
Uso del método error para escribir información de excepción en el Infolog
En el ejemplo de código siguiente se usa el método error para escribir información de excepción en Infolog.
// This example shows that the use of the Global::error method
// is a reliable way to display exceptions in the Infolog.
static void TryCatchGlobalError2Job(Args _args)
{
/***
The 'Global::error()' does directly add a message to the Infolog.
The exception is caught.
***/
try
{
info("In the 'try' block. (j2)");
throw Global::error("Written to the Infolog.");
}
catch (Exception::Error)
{
info("Caught 'Exception::Error'.");
}
/*** Infolog output
Message (03:51:44 pm)
In the 'try' block. (j2)
Written to the Infolog.
Caught 'Exception::Error'.
***/
}
Manejo de un CLRError
En el ejemplo de código siguiente se controla una excepción CLRError .
// This example shows that a CLRError exception is not displayed
// in the Infolog unless you catch the exception and manually
// call the info method. The use of the CLRInterop::getLastException
// method is also demonstrated.
static void TryCatchCauseCLRError3Job(Args _args)
{
/***
The 'netString.Substring(-2)' causes a CLRError,
but it does not directly add a message to the Infolog.
The exception is caught.
***/
System.String netString = "Net string.";
System.Exception netExcepn;
try
{
info("In the 'try' block. (j3)");
netString.Substring(-2); // Causes CLR Exception.
}
catch (Exception::Error)
{
info("Caught 'Exception::Error'.");
}
catch (Exception::CLRError)
{
info("Caught 'Exception::CLRError'.");
netExcepn = CLRInterop::getLastException();
info(netExcepn.ToString());
}
/********** Actual Infolog output (truncated for display)
Message (03:55:10 pm)
In the 'try' block. (j3)
Caught 'Exception::CLRError'.
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. --->
System.ArgumentOutOfRangeException: StartIndex cannot be less than zero.
Parameter name: startIndex
at System.String.InternalSubStringWithChecks(Int32 startIndex, Int32 length, Boolean fAlwaysCopy)
at System.String.Substring(Int32 startIndex)
at ClrBridgeImpl.InvokeClrInstanceMethod(ClrBridgeImpl* , ObjectWrapper* objectWrapper, Char* pszMethodName,
Int32 argsLength, ObjectWrapper** arguments, Boolean* argsAreByRef, Boolean* isException)
**********/
}
Uso de una instrucción retry
En el ejemplo de código siguiente se usa una instrucción retry .
// This example shows how to use the retry statement. The print
// statements are included because retry causes earlier Infolog
// messages to be erased.
static void TryCatchRetry4Job(Args _args)
{
/***
Demonstration of 'retry'. The Infolog output is partially erased
by 'retry', but the Print window is fully displayed.
***/
Exception excepnEnum;
int nCounter = 0;
try
{
info(" .");
print(" .");
info("In the 'try' block, [" + int2str(nCounter) + "]. (j4)");
print("In the 'try' block, [" + int2str(nCounter) + "]. (j4)");
nCounter++;
if (nCounter >= 3) // Prevent infinite loop.
{
info("---- Will now throw a warning, which is not caught.");
print("---- Will now throw a warning, which is not caught.");
throw Global::warning("This warning will not be caught. [" + int2str(nCounter) + "]");
}
else
{
info("Did not throw a warning this loop. [" + int2str(nCounter) + "]");
print("Did not throw a warning this loop. [" + int2str(nCounter) + "]");
}
excepnEnum = Global::error("This error message is written to the Infolog.");
throw excepnEnum;
}
catch (Exception::Error)
{
info("Caught 'Exception::Error'.");
print("Caught 'Exception::Error'.");
retry;
}
info("End of job.");
print("End of job.");
/********** Actual Infolog output
Message (04:33:56 pm)
.
In the 'try' block, [2]. (j4)
---- Will now throw a warning, which is not caught.
This warning will not be caught. [3]
**********/
}
Lanzar una excepción dentro de una transacción
En el ejemplo de código siguiente se produce una excepción en un bloque de transacciones.
// This examples uses three levels of try nesting to illustrate
// where an exception is caught when the exception is thrown inside
// a ttsBegin ... ttsCommit transaction block.
static void TryCatchTransaction5Job(Args _args)
{
/***
Shows an exception that is thrown inside a ttsBegin - ttsCommit
transaction block cannot be caught inside that block.
***/
try
{
try
{
ttsbegin;
try
{
throw error("Throwing exception inside transaction.");
}
catch (Exception::Error)
{
info("Catch_1: Unexpected, caught in 'catch' inside the transaction block.");
}
ttscommit;
}
catch (Exception::Error)
{
info("Catch_2: Expected, caught in the innermost 'catch' that is outside of the transaction block.");
}
}
catch (Exception::Error)
{
info("Catch_3: Unexpected, caught in 'catch' far outside the transaction block.");
}
info("End of job.");
/********** Actual Infolog output
Message (04:12:34 pm)
Throwing exception inside transaction.
Catch_2: Expected, caught in the innermost 'catch' that is outside of the transaction block.
End of job.
**********/
}
Uso de Global::error con un parámetro SysInfoAction
Cuando el código produce una excepción, puede escribir mensajes en Infolog. Puede hacer que esos mensajes de Infolog sean más útiles mediante la clase SysInfoAction .
En el siguiente ejemplo, pasas un parámetro SysInfoAction al método Global::error . El método de error escribe el mensaje en el Infolog. Cuando el usuario hace doble clic en el mensaje Infolog, se ejecuta el método SysInfoAction.run .
En el método run , puede escribir código que ayude a diagnosticar o corregir el problema que provocó la excepción. El objeto que pasas al método Global::error se construye a partir de una clase que escribes y que extiende SysInfoAction.
El siguiente ejemplo de código se muestra en dos partes.
- La primera parte muestra un trabajo que llama al método Global::error y, a continuación, produce el valor devuelto. Pasas una instancia de la clase SysInfoAction_PrintWindow_Demo al método de error .
- La segunda parte muestra la clase SysInfoAction_PrintWindow_Demo .
Parte 1: Llamar a Global::error
static void Job_SysInfoAction(Args _args)
{
try
{
throw Global::error
("Click me to make the Print window display."
,""
,new SysInfoAction_PrintWindow_Demo()
);
}
catch
{
warning("Issuing a warning from the catch block.");
}
}
Parte 2: La clase SysInfoAction_PrintWindow_Demo
public class SysInfoAction_PrintWindow_Demo extends SysInfoAction
{
str m_sGreeting; // In classDeclaration.
public str description()
{
return "Starts the Print Window for demonstration.";
}
public void run()
{
print("This appears in the Print window.");
print(m_sGreeting);
/*********** Actual Infolog output
Message (03:19:28 pm)
Click me to make the Print window display.
Issuing a warning from the catch block.
***************/
}
public container pack()
{
return ["Packed greeting."]; // Literal container.
}
public boolean unpack(container packedClass, Object object = null)
{
[m_sGreeting] = packedClass;
return true;
}
}
Lista de excepciones
En la tabla siguiente se muestran los literales de excepción que son los valores de la enumeración Exception .
| Literal de excepción | Descripción |
|---|---|
| Quebrar | El usuario presionó Break o Ctrl+C. |
| CLRError | Se ha producido un error mientras se usaba la funcionalidad CLR. |
| CodeAccessSecurity | Se ha producido un error mientras se usaba el método CodeAccessPermission.demand . |
| DDEerror | Se produjo un error mientras se usaba la clase del sistema DDE . |
| Estancamiento | Se produjo un interbloqueo de la base de datos, porque varias transacciones se esperan entre sí. |
| DuplicateKeyException | Se ha producido un error en una transacción que usa el control de simultaneidad optimista. La transacción se puede reintentar (use una instrucción retry en el bloque catch ). |
| DuplicateKeyExceptionNotRecovered | Se ha producido un error en una transacción que usa el control de simultaneidad optimista. El código no se volverá a intentar. Esta excepción no se puede detectar dentro de una transacción. |
| Error | Se ha producido un error grave. La transacción se ha detenido. |
| Información | Este literal de excepción contiene un mensaje para el usuario. No lance una excepción de información . |
| Interno | Se produjo un error interno en el sistema de desarrollo. |
| Numérico | Se produjo un error mientras se usaba la función str2int, str2int64 o str2num . |
| Secuencia | |
| UpdateConflict | Se ha producido un error en una transacción que usa el control de simultaneidad optimista. La transacción se puede reintentar (use una instrucción retry en el bloque catch ). |
| UpdateConflictNotRecovered | Se ha producido un error en una transacción que usa el control de simultaneidad optimista. El código no se volverá a intentar. Esta excepción no se puede detectar dentro de una transacción. |
| Advertencia | Ha ocurrido un evento excepcional. Aunque es posible que el usuario tenga que tomar medidas, el evento no es fatal. No lance una excepción de advertencia . |
| Error de conexión SQL Excepción X++ | Se produjo un error durante la ejecución de la consulta. La transacción será cancelada. Esta excepción no se puede detectar dentro de una transacción. |
| Interrupción | Se agotó el tiempo de espera de ejecución de la consulta SQL. La excepción no se puede detectar dentro de una transacción. La excepción se puede volver a intentar mediante una instrucción retry en el bloque catch. |