The X++ container is a powerful and somewhat odd datatype. It’s like an array but supports different data types in the same instance and it can grow infinitely large. Also containers don’t have methods like real objects; you need to call special functions and pass your container to them.
Many data structures in X++ are serialized (packed) into containers before they’re sent across the network or stored in the database. The entire SysLastValue framework runs on it.
Unfortunately this kind of flexibility is often abused. It’s very easy to abuse containers and I see it happen too often. In this article I’ll go over some good practices for containers. I won’t go into basics of containers. You can find basic documentation on MSDN (methods starting with con).
Appending to containers
Most of the time you want to append something to a container. Typically I come across code like this:
for (i=1; i<=maxNum; i++)
con = conIns(con, i, theValue);
Don’t do that. The conIns() function is slow because it copies the original container to a new one before returning.
The above should be rewritten as:
for (i=1; i<=maxNum; i++)
con += theValue;
It’s a lot faster and easier to read as well.
Only use conIns() if there’s no other option, i.e. for inserting values not at the end. Even adding elements to the front of the container is slow because the existing container is still copied. If at all possible write your code in such a way that you only append elements using +=. This is the fastest way to do it as it modifies the original container in place. And it’s easier to read as well.
Whenever you’re going to insert a fixed set of variables into the container you can add them all at once. A typical example when writing something to a file.
con = conNull();
con += 1;
con += someValue;
con += otherValue;
con += "X";
This is works but there’s a better way. You can assign a bunch of values to the container in a single line. Try this instead:
con = [1, someValue, otherValue, "X"];
This is a contrived example and often the complexity filling the container can be pushed into a separate method. Usually hiding details like that increases readability of the code. It isn’t always possible to do it like this but simplify your code whenever possible.
The above trick works the other way around too. You can write this:
a = conPeek(con, 1);
b = conPeek(con, 2);
c = conPeek(con, 3);
This can be done in a simpler way:
[a, b, c] = con;
Bonus points if you can explain this:
[a, b] = [b, a];
Passed by reference
Containers are a primitive data type in Ax, so they’re passed by value. That means containers will be copied when passed as a function argument. Keep that in mind when you’re dealing with containers that can become large. For small containers the performance hit is negligible but at some point you’re going to notice it.
Don’t mimic other data structures with containers
Too often I see a mess of containers that could be replaced by a much more appropriate data structure.
Although it’s very easy and tempting to use containers for everything, again, it’s not a good idea. Other developers may have a hard time understanding these ad hoc data structures. Even understanding your own code can be a problem if you haven’t worked on it for a while.
There are alternatives to containers. Take a look at the Foundation Classes. They’re somewhat like the .NET collections. You can use a List, Map, Set, Array and Struct. They can be packed into containers when necessary.
If all variables are of the same type, consider using a List (ordered) or Set (unordered). If you need to track related values use Maps (key/value pairs) or Structs (supports different complex types). Please don’t use nested containers or keep values together in several containers at the same index. It’s hard to read and easy to break.