Creating standard exceptions

One of things I really miss in X++ is any reasonable way to handle exceptions. AX doesn’t know exceptions as objects, it has only an enumeration type Exception and, morover, programmers should basically throw only Error, UpdateConflict and UpdateConflictNotRecovered explicitely, which effectively prevents any handling based on the type of exception.

We can hardly do anything with X++ features, but there is another thing related to objects of exception, which is resolvable: Many exceptions can be qualified as exceptions of the same standard type (e.g. an unexpectedly empty field in a buffer), but one still must find/remember a suitable error message for each such type. I would appreciate something like in C#:

throw new ArgumentNullException("itemId");
//Exception message:
//Value cannot be null.
//Parameter name: itemId

Simply, not to care about the exact error message and to have to remember the right type of exception only.

I made one experimental implementation (link to .xpo at the end) – please note that it is more an illustration to think about than a completed library of exceptions.

Originally I started with a bit more complicated design, but is crystalized to a very simple interface during the refactoring. Basic usage looks like this:

throw exception(new ArgumentEmptyException('_itemId'));

Quite readable, isn’t it?

The exception is a method in the Global class, equivalent to the error() method, but accepting a single parameter only – an object of the SysException type. As error(), it returns a value of the Exception enumeration type, which is subsequently used in the throw statemement.

The base of the exception hierarchy is the SysException class. It has the following main members:

class SysException
{
    new() //parameterless constructor
    str message() //returns error message
    Exception show() //write exception to infolog
}

SysException contains a basic implementation so the derived exceptions don’t need to override any methods (although they should). The class of exception in the following example declares nothing but extends SysException:

throw exception(new DeveloperIsDumbheadException());
//Infolog: Exception of type 'DeveloperIsDumbheadException' was thrown.

Exceptions can provide their own text by overriding the message() method.

public str message(){ return "Oops"; }

In fact, SysException has two more properties, HelpUrl and InfoAction. They can be set either by accessor methods or by using a method chaining. My effort was to allow to set all properties by accessor methods, but make it unnecessary.

//using a usual accessor method
SysException ex = new DeveloperIsDumbheadException();
ex.helpUrl("http://thedailywtf.com");
throw exception(ex);
 
//the same thing via a method chaining
throw exception(new DeveloperIsDumbheadException().withHelpUrl("http://thedailywtf.com"));

Constructors of derived classes can accept optional parameters, as in the case of ArgumentEmptyException mentioned at the beginning.

In the following example you can find all derived exceptions contained in the .xpo. There is not many of them, because they only served for the architecture verification so far.

//Infolog: Value of parameter '_userId' is not valid.
throw exception(new ArgumentException("_userId"));
 
//Infolog: Parameter '_userId' cannot be empty.
throw exception(new ArgumentEmptyException("_userId"));
 
//Infolog: Field EmplId must be filled in.
throw exception(new FieldEmptyException(fieldStr(EmplTable, EmplId)));
 
switch (numberSeqModuleEnumValue)
{
    //(...) processing of known elements
    default: //invalid value or value not known at the time of program writing
        //Infolog: Enum element 'PRODCOM' is not valid.
        throw exception(new InvalidEnumException(numberSeqModuleEnumValue));
}

It’s clear that my goal wasn’t to replace all exception throwing – if you need a specialized error message, use the same approach as before. But if you are throwing a “standardized” exception, you can use an object of SysException type and don’t bother with the construction of the error message. Potentially you will also be able to change the label on a single place, to log a specific type of errors and so on.

The source code is available here.

Just a note, most of these exceptions use hard-coded texts and not labels and the .xpo contains two standard objects: classes Global and SysInfoAction_Editor.