The name "Axapta Foundation Classes" or just AFC for short, is given to a number of classes that are used to contain other values. Each one specifies the type of the constituent element. The foundation classes have many things going for them: They are easy to use and very effective; because there is very little overhead they scale well, both in terms of memory consumption and speed. They are used pervasively in the application code. I will present the individual classes and then ask the burning question where I need your help.
They main classes are:
These are name / value pairs that can be built and queried at runtime. They can be very useful for a number of situations where a class might otherwise be used. The example below creates a struct with two fields, adds a values and retrieves them:
struct s = new struct('int age; str name');
s.Value('name', 'John Doe');
print strFmt('%1 : %2 years of age', s.Value('name'), s.Value('age'));
Arrays are (as the name implies) arrays of elements that are accessed by index. They grow as required, leaving default values of the given type for hitherto unused elements. They're very useful because they can contain reference types (i.e. object instances and tables), which normal X++ arrays cannot. They can also easily be passed as parameters and be returned as return values from methods, something that also eludes the normal X++ arrays.
array a = new array(Types::integer); // Array of ints
for (i = 1; i < 100; i++)
print CalculateSum(a); // Pass as parameter
Lists are simply a sequence of objects. List elements can be inserted at the start or at the end of the list. There is no ordering of the elements other than the order in which they were inserted.
list names = new list(types::string);// List of strings
Sets are used to contain elements in such a way that there is only a single element of a given value at a given time. It is also guaranteed that traversal of the set using enumerators and iterators will return the elements in order, smaller before larger.
set uniqueInts = new set(types::integer);
uniqueInts.Add(11); // Already there.
se = uniqueInts.GetEnumerator();
Maps are structures that map one value onto another. People using the .NET framework will know these as SortedDictionary<TDomain, TRange>. As for sets, they can be traversed with enumerators and will always return smaller values before larger ones. Operations are available to look a value in the range set given a domain value, and to check whether any given value exists in the domain.
Map intToString = new Map(types::integer, types::string);
I have deliberatly not gone into enumerators and iterators, because they are not the issue in this blog. You can find more information about the container classes and how they can be used in http://msdn.microsoft.com/en-us/library/ms941640.aspx.
Now, the question that I need your help on is this: The AFC classes as mentioned above accept NULL values to be inserted into them. For instance, if you are maintaining a set of query objects, then it is perfectly legal to insert NULL into such a set:
Set qs = new Set(Types::Class);
Query q = null;
qs.Add(q); // Perfectly legal.
print qs.elements(); // == 3
The set will now contain three elements, two queries and a null value. The value is a bona fide value: Traversing the set will return the null value just as any other value. Along a similar vein, it is possible to map null onto something in a map, to have list and array elements that are null.
It makes good sense to me that it should be possible to insert NULL into individual array elements, and even into a particular list element. Also, setting a value in a struct to null does not seem offensive. However, I have reservations when it comes to storing nulls in sets and maps. The fact that this is possible is not based on a particular user scenario that we wanted to support back in the days where this was implemented: It is merely a result of the implementation that we happened to choose at the time.
This behavior is now hurting us in some work that we want to do in the implementation of sets and maps. It is difficult and expensive for us to maintain this behavior given some changes we want to make. The question, then, is this: Is anyone actually relying on this behavior in their application code? The .NET collection classes do not support this behavior and will immediately throw an exception if this is attempted. We are tempted to do the same.
Please take the time to provide feedback: It will make a difference in how we proceed with this. Ping pvillads with your opinion