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 movido de Yammer a Microsoft Viva Engage. Para unirse a una comunidad viva Engage y participar en las últimas discusiones, rellene el formulario Solicitar acceso a Finance and Operations Viva Engage Community y elija la comunidad a la que desea unirse.
En este artículo se describe el control de excepciones en X++. Controle los errores mediante el lanzamiento, pruebe... capture, finalmente y vuelva a intentar instrucciones para generar y controlar excepciones.
Una excepción es un salto regulado de la secuencia de ejecución del programa. Los try...catch bloques y el tipo de excepción que se produce determinan la instrucción donde se reanuda la ejecución del programa. Una excepción se representa mediante un valor de la enumeración Exception o una instancia de . La clase de System.Exception NET o una clase derivada de ella. Una excepción que a menudo se produce es el valor de enumeración Exception::error . Una práctica habitual es escribir información de diagnóstico en infolog antes de iniciar la excepción.
El método Global::error suele ser la mejor manera de escribir información de diagnóstico en infolog. Por ejemplo, el método podría 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 conocer necesariamente la ubicación del bloque catch que recibe el control cuando se produce la excepción.
instrucciones throw
Use la palabra clave throw para iniciar un valor de enumeración exception . Por ejemplo, la siguiente instrucción produce una excepción de error.
throw Exception::error;
En lugar de iniciar un valor de enumeración, use la salida del método Global::error como operando para throw.
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");
Puede llamar a los métodos estáticos en la clase Global sin el prefijo Global:: . Por ejemplo, puede llamar al método Global::error como este.
error('My message.');
Use la palabra clave throw para iniciar excepciones de .NET, así como los valores del tipo de excepción enumerado.
throw new System.InvalidOperationException("This function is not allowed");
Use la palabra clave throw por sí misma dentro de un bloque catch. En este caso, throw se comporta como la instrucción rethrow en C#. La excepción original, el mensaje de excepción y su contexto, como la pila de llamadas, se vuelven a iniciar y están disponibles para cualquier instrucción catch 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 se produce una excepción, el sistema lo procesa primero a través de la lista catch del bloque try más interno. Si el sistema encuentra un bloque catch que controla el tipo de excepción que se produjo, el control de programa salta a ese bloque catch . Si la lista catch no tiene ningún bloque que especifica la excepción, el sistema pasa la excepción a la lista catch del bloque try más interno. El sistema procesa las instrucciones catch en la misma secuencia que aparecen en el código.
Es una práctica habitual que la primera instrucción catch controle el valor de enumeración Exception::Error . Una estrategia consiste en que la última instrucción catch deje sin especificar el tipo de excepción. 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 adecuada para el intento más externo... bloques catch .
Puede incluir una cláusula finally opcional en try... instrucciones catch . La semántica de una cláusula finally es la misma que en C#. El sistema ejecuta las instrucciones en la cláusula finally cuando el control sale del bloque try , 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. Use la instrucción retry cuando el código del bloque catch pueda corregir la causa de la excepción. La instrucción retry proporciona al código en el bloque try otra oportunidad para que se realice correctamente. La instrucción retry borra todos los mensajes que se han escrito en infolog desde que el control de programa entró en el bloque try .
Nota:
Debe asegurarse de que las instrucciones de reintento no provocan un bucle infinito. Como procedimiento recomendado, el bloque try debe incluir una variable que puede 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 instrucción catch controla la excepción, el controlador de excepciones del sistema lo controla. El controlador de excepciones del sistema no escribe en infolog. Por lo tanto, las excepciones no controladas pueden ser difíciles de diagnosticar. Siga todas estas instrucciones para proporcionar un control de excepciones eficaz:
- Tenga un bloque try que contenga todas las instrucciones del marco más externo de la pila de llamadas.
- Tener un bloque catch sin calificar al final de la lista de capturas más externa.
- Evite iniciar directamente un valor de enumeración exception .
- Inicie el valor de enumeración que devuelve uno de los métodos siguientes en la clase Global : Global::error, Global::warning o Global::info. (Puede omitir el prefijo Global:: implícito).
- Cuando detecta una excepción que el infolog no muestra, llame a la función Global::info para mostrarla.
Exception::CLRError, Exception::UpdateConflictNotRecovered y las excepciones del kernel del sistema son ejemplos de excepciones que el Infolog no muestra automáticamente.
Excepciones e interoperabilidad clR
Puede llamar a las clases y métodos de Microsoft .NET Framework que residen en ensamblados que administra Common Language Runtime (CLR). Cuando el código inicia una instancia system.Exception de .NET Framework, puede capturarla declarando una variable de tipo System.Exception para detectar cualquier excepción de .NET o puede detectar un tipo de excepción de .NET específico mediante una de sus clases derivadas, como se muestra en el ejemplo siguiente.
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);
}
Puede detectar excepciones de .NET haciendo referencia a Exception::CLRError. El 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
Infolog no muestra excepciones del tipo Exception::CLRError , ya que 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
El código siguiente 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 de error no produce una excepción. Solo proporciona un valor de enumeración que puede usar en una instrucción throw . La instrucción throw produce la excepción. Estas son las descripciones de los parámetros del método de error . Solo se requiere el primer parámetro.
- SysInfoLogStr txt es una cadena 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 omite si se proporciona _sysInfoAction.
- SysInfoAction es una instancia de una clase que extiende la clase SysInfoAction. Los reemplazos 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
Use el método Global::info para mostrar texto en el Infolog. En programas, escríbalo como info("Mi mensaje.");. Aunque el método de información devuelve un valor de enumeración Exception::Info , rara vez desea iniciar Exception::Info, porque no se produjo nada inesperado.
Método Global::exceptionTextFallThrough
En ocasiones, quieres hacer nada dentro de tu bloque catch . 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 declara explícitamente la intención.
Excepciones dentro de 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 a las dos excepciones que se producen manualmente y 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 transacción puede procesar la excepción (a menos que sea updateConflict o 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 instrucción catch como esta, catch (Exception::DuplicateKeyException). Una instrucción catch{} general no puede detectar UpdateConflict ni DuplicateKeyException dentro de una transacción.
La cláusula finally se ejecuta incluso en el ámbito de transacción.
Excepciones e using instrucciones
El ámbito de excepción no afecta a la semántica de using las instrucciones. La using instrucció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 excepciones en infolog.
// 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 de error para escribir información de excepción en infolog
En el ejemplo de código siguiente se usa el método de 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'.
***/
}
Control de 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 de reintento
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]
**********/
}
Iniciar 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 ejemplo siguiente, se pasa un parámetro SysInfoAction al método Global::error . El método de error escribe el mensaje en 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 se pasa al método Global::error se construye a partir de una clase que se escribe que extiende SysInfoAction.
El ejemplo de código siguiente 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. Se pasa 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 | Description |
|---|---|
| Inter | El usuario ha presionado Interrumpir o Ctrl+C. |
| CLRError | Error al usar la funcionalidad CLR. |
| CodeAccessSecurity | Error al usar el método CodeAccessPermission.demand . |
| DDEerror | Error al usar la clase del sistema DDE . |
| Deadlock | Se produjo un interbloqueo de base de datos, ya que varias transacciones se esperan entre sí. |
| DuplicateKeyException | 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 | Error en una transacción que usa el control de simultaneidad optimista. El código no se reintentará. Esta excepción no se puede detectar dentro de una transacción. |
| Error | Error irrecuperable. Se ha detenido la transacción. |
| Información | Este literal de excepción contiene un mensaje para el usuario. No inicie una excepción de información . |
| Interno | Error interno en el sistema de desarrollo. |
| Numeric | Error mientras se usaba la función str2int, str2int64 o str2num . |
| Sequence | |
| UpdateConflict | 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 | Error en una transacción que usa el control de simultaneidad optimista. El código no se reintentará. Esta excepción no se puede detectar dentro de una transacción. |
| Advertencia | Se ha producido un evento excepcional. Aunque es posible que el usuario tenga que tomar medidas, el evento no es irrecuperable. No inicie una excepción de advertencia . |
| Excepción X++ de error de conexión SQL | Se produjo un error cuando durante la ejecución de la consulta. La transacción se cancelará. Esta excepción no se puede detectar dentro de una transacción. |
| Timeout | Se agota el tiempo de espera de ejecución de consultas SQL. La excepción no se puede detectar dentro de una transacción. La excepción se puede reintentar mediante una instrucción retry en el bloque catch. |