X++ was designed to be a superset of Java with strong data access features. In some ways the data access features were like LINQ, but it is much older and in some ways more powerful. For example, X++ supports set-based manipulation of data via the insert_recordset, update_recordset, and delete_from keywords. These map to Insert, Update, and Delete statements in SQL. While this generally offers the best performance, you can also use a hybrid approach that combines set-based access with imperative-style statements.
In the above code the statement “myTable.AmountMST += 100;” is actually a set-based operation that will affect all records that match the preceding where clause.
Another difference is that unlike LINQ, which relies on proxy objects created with an ORM, X++ has real compile-time access to the database’s metadata. This allows for much stricter static checking than C# or VB can provide. Before we continue let us take a moment to consider what makes that possible.
Like many embedded languages, X++ originally had a completely proprietary stack. The IDE, compiler, p-code format, and interpreter were specific to IBM Axapta, the product that would eventually become Microsoft Dynamics AX. Since X++ code was almost always written for a specific installation of Axapta, they could tie the compiler directly to the database. This meant that when someone accessed the Customers table class they were literally accessing the Customers table.
Starting with Dynamics AX 2009, X++ gained a second compiler built upon the .NET stack. This lead to some interesting problems. Like older versions of Visual Basic, X++ used deterministic garbage collection. Unfortunately the garbage collector it was using had some severe performance problems when there were more than half a million live objects. After discounting the idea of rewriting the garbage collector from scratch, they decided to instead leverage the one in the .NET runtime.
Moving from the proprietary runtime to .NET posed its own challenges. The X++ team is quite small and they didn’t think they could get funding to rewrite the compiler from end-to-end. So instead they started at the p-code level. The same compiler is used, but instead of interpreting the p-code they instead run a tool that converts it to IL code. Both p-code and IL are stack-based languages so the translation was surprisingly easy.
In addition to deterministic garbage collection, X++ also lost an interesting client-server feature. Like DCOM or .NET Remoting, objects in X++ could live on both the server and the client at the same time. Any client-side method calls on one of these classes would then travel across the wire for execution, allowing for some interesting architectural designs. Unfortunately this chatty, stateful design simply doesn’t scale well so the X++ team wanted to stop using it anyways.
In some areas X++ has less type safety than Java. For example, the expression in an “if” statement need not be a Boolean: non-zero integers and non-null objects are evaluated as true. The = operator will try to perform an implicit conversion even if data loss is possible. Casting from a base type to a derived type was also implicit prior to AX 2012.
String handling in X++ is different too. Strings may be delaminated by either single or double-quotation marks and comparisons are not case sensitive. X++ does not have a char data type, but one can declare fixed length strings of size 1. Like most c-based languages, X++ does make the mistake of using the addition operator for concatenation. Like C#, string literals will use the backslash (\) for escape sequences unless the string is prefixed by the at sign (@).
Local variables have to be declared before any statements in an X++ method. This includes loop variables, making the syntax slightly different than other C-based languages. While statements are the same and support both continue and break.
The switch statement in X++ supports multiple values and expressions for cases. Each constant or expression on a given case is separated by a comma. X++ does not support ranges (e.g. case is > 5), placing it between VB and C# in the power curve.
X++ has both enumerators and iterators. An iterator in X++ is like an iterator in Java, not only can it navigate the collection it can also add and remove items as it goes. The X++ enumerator is like the IEnumerator in .NET, it is a read-only view that will be fouled if the collection is modified.
Error handling is mostly based on the structured exception handling found in Java and C# with three notable distinctions. An X++ exception is really just a numeric error code so it lacks the rich information found in the aforementioned languages. X++ also has a retry statement, which is effectively a goto that jumps to the start of the closest try block. This can be quite useful for handling intermittent errors such as dead locks. X++ does not have finally blocks, which could be a problem considering that it no longer had deterministic garbage collection either.
X++ supports inheritance and method overriding, but not overloading. To simulate overloading one has to use optional parameters. Like Java, methods are virtual by default but can be sealed using the “final” keyword.