Macros in X++ are precompiler directives that let you define reusable symbols, conditions, or blocks of code. These are processed before the X++ compiler sees the code. Although macros are still supported, their use is discouraged and retained mainly for backward compatibility.
⚠️ Recommended Alternatives:
Use constants,
SysDa
for queries, and standard X++ constructs instead of macros.
Common Directives
#define
– Defines a macro, optionally with a value.#if
– Checks if a macro is defined or equals a value.#undef
– Removes a macro defined earlier.#localmacro
/#macro
– Defines multi-line macros, terminated with#endmacro
.#globaldefine
/#globalmacro
– Global versions, but not recommended due to conflict risk.#macrolib
– Imports macro libraries from the AOT.#linenumber
– Shows the line number in code during debugging.
Macro Values & Parameters
Macro values are just character sequences, not typed variables.
Use parentheses to assign values:
#define.MyMacro(100)
Use
%1
,%2
, etc., for macro parameters:
#define.PrintInfo("Name: %1, Age: %2")
info(#PrintInfo("Alice", "30"));
Special Use Directives
#defInc
and#defDec
increase/decrease numeric macro values.Only works with simple integer values (not strings or hex).
Scope and Inheritance
Macros follow class inheritance: parent macros are available to child classes.
The precompiler processes all class-level macros first, then method-level.
Best Practices
Prefer
#define
and#localmacro
over global variants.Always assign values to macros for clarity.
Avoid mixing macro value modes (with/without values).
Don’t use macros for logic where native X++ constructs are more readable and safe.
Example
#define.CompanyName("Contoso")
static void Job1(Args _args)
{
info("Company: " + #CompanyName);
}
Macros can be useful, but modern X++ development should rely on cleaner and more structured approaches wherever possible.