Collection Classes in Microsoft Dynamics AX
The
X++ language syntax provides two compound types: arrays and containers. They
are useful for aggregating values of simpler types. However, you cannot store
objects in arrays or containers. The Microsoft Dynamics AX collection classes have
been designed for storing objects. The classes are implemented in C++ to
achieve the maximum performance (they are system classes).
Note
|
Collection classes were formerly called Foundation
classes.
|
Collection Classes
The
collection classes are shown in the following table.
Class
|
Description
|
Array
|
Similar to the X++ language array type except that it
can hold values of any single type, including objects and records. Objects
are accessed in a specific order.
For more information, see X++, C# Comparison: Array Syntax.
|
List
|
Contains elements that are accessed sequentially.
Unlike the Array class, the List class provides an addStart method.
As with the Set class, the List class provides methods getEnumerator and getIterator. You can use an iterator to insert and delete items
from a List object.
|
Map
|
Associates a key value with another value.
|
Set
|
Holds values of any single type. Values are not stored
in the sequence they are added. Instead, the Set object stores them in a manner that optimizes
performance for the in method.
When you add a value to a Set object which is already storing that same value, the
add attempt is ignored by the Set object.
Unlike the Array class, the Set class provides the methods in and remove.
|
Struct
|
Can contain values of more than one type. Used to group
information about a specific entity.
|
Types Stored in Collections
The
constructor for each collection class, except Struct,
takes in a parameter value that is an element of the Types system enum. The collection instance can
store items of that type only.
The Types::AnyType enum element is a special case and it
cannot be used to construct a usable collection object, such as a Set object.
The null value cannot be stored as an element in a Set object. And null cannot be a key in a Map object.
Do Not Change Elements During
Iteration
You
can iterate through an X++ collection object with an iterator or enumerator.
Here are typical examples of how you can obtain an iterator:
·
new MapIterator(myMap)
·
myMap.getEnumerator()
For Set objects, if any elements are added or
removed after an iterator is created, the iterator instance can no longer be
used to read from or step through the collection.
For Map objects, element removals invalidate the
iterator just as for Set objects. However in Microsoft Dynamics AX
2012, a MapIterator object remains valid even after a call to
the Map.insert method. This is true whether the key is
new, or whether the key already exists and only the value is actually being
updated in the Map element. X++ code that calls Map.insert and relies on the iterator object
remaining valid might fail if run as .NET Framework CIL.
For
more information, browse to the MSDN
blogs and search for the X++ blog
post titled Changes to the Collection Classes in
Dynamics.
Extend a Collection Class
You
can use the collection classes to form more complex classes. For example, a
stack might easily be implemented by using a list where the elements are always
added to the start of the list. The newest element then occupies the top of the
stack.
You
can also extend the collection classes. For example, you might extend the List class to create a list of customer records
where the operations could be made type safe. The derived collection class
would accept only customer records, not just any record.
The following example
creates an array of classes and adds three query objects to the array.
X++
{
Array
oarray = new Array (Types::Class);
oarray.value(1, new query());
oarray.value(2, new query());
oarray.value(4, new query());
print
oarray.toString();
print
oarray.definitionString();
pause;
}
The following example
creates a list of integers and prints out a description of the list and the
values that it contains.
X++
{
//
Create a list of integers
List
il = new List(Types::Integer);
// Add
some elements to the list
il.addEnd(1);
il.addEnd(2);
il.addStart(3);
//
Print a description of the list
print
il.definitionString();
print
il.toString();
pause;
}
X++
{
Map
example;
Map
invertMap(map _mapToInvert)
{
MapEnumerator en;
Map result = new Map(
_mapToInvert.valueType(),
_mapToInvert.keyType());
if
(_mapToInvert.keySet().elements()
!= _mapToInvert.valueSet().elements())
{
return null;
}
en
= new MapEnumerator(_mapToInvert);
while (en.moveNext())
{
result.insert(en.currentValue(), en.currentKey());
}
return result;
}
;
//
Fill in a few values.
example = new Map(Types::Integer, Types::String);
example.insert (1, "one");
example.insert (2, "two");
print
invertMap(example).toString();
pause;
// Now
two keys (2 and 3) map to the same value
// so
can't create inverse map
example.insert (3, "two");
if
(!invertMap(example))
{
print "Could not create the map";
}
pause;
}
The following example checks
whether any of the values in one set, the noConfigs set, are found in a second set, the yesConfigs set. If they are, they are removed from the yesConfigs set.
X++
Set
noConfigs;
Set
yesConfigs;
ConfigId
configId;
SetEnumerator
se;
;
se = noConfigs.getEnumerator();
while (se.moveNext())
{
configId = se.current();
if
(yesConfigs.in(configId))
{
yesConfigs.remove(configId);
}
}
The following example
creates a struct with two items and then assigns values to those items. A new
item is then added by using the Struct.addmethod; the data type of the item is inferred from the value
assigned to it. The struct is then packed into a container and used to create a
new struct, a copy of the original struct.
X++
{
Struct
s = new struct ("int age; str name");
Struct
copy;
container c;
int i;
// Add
values to the items
s.value("age", 25);
s.value("name", "Jane Dow");
// Add a
new item; data type is inferred from value
s.add("Shoesize", 45);
//
Print the definition of the struct
print
s.definitionString();
//
Prints the type and name of all items in the struct
for (i
= 1;
i <= s.fields();i++)
{
print s.fieldType(i), " ", s.fieldName(i);
}
//
Pack the struct into a container and restore it into copy
c =
s.pack();
copy =
Struct::create(c);
print
copy.definitionString();
pause;
}