четверг, 8 марта 2018 г.

Классы коллекций в Dynamics AX


Введение

Для работы со структурами данных в Dynamics AX предусмотрены несколько классов коллекций, которые позволяют хранить и извлекать не только данные, но и записи, и объекты.
Каждый класс имеет свои ограничения на хранимые типы данных и доступ к ним, в таблице ниже они описаны:

Класс
Описание
Тип данных
Доступ
List
Значения хранятся последовательно с возможностью добавления новых значений в начало или конец списка.
Все значения должны быть одного типа
Используется ListIterator или ListEnumerator
Set
Значения хранятся неупорядоченно. Повторяющиеся значения не добавляются.
Все значения должны быть одного типа
Используется SetIterator или SetEnumerator
Map
Значения имеют уникальный ключ.
Ключи должны быть одного типа. Значения для каждого ключа также должны быть одного типа.
Используется MapIterator или MapEnumerator
Array
Значения имеют уникальный числовой ключ.
Все значения должны быть одного типа
Доступ осуществляется по числовому ключу.
Struct
Значения имеют уникальный ключ строкового типа
Ключ должен быть строковым. Значения могут быть любого типа.
Доступ осуществляется по строковому ключу.

По сравнению с контейнерами, классы коллекций имеют более функциональный интерфейс.
Все классы коллекций содержатся в памяти, поэтому, при работе с ними, необходимо учитывать их размер. Если необходимо обработать большой объем данных, то следует рассмотреть альтернативные варианты, например, временные таблицы.
Рассмотрим сами классы ниже.

List (Список)

В списке можно хранить данные одного типа в упорядоченной последовательности. Элементы могут быть любого типа X++. Тип определяется при создании и не может быть изменен после инициализации. Элементы могут добавляться в начало или конец списка. Основные методы для работы со списками описаны в таблице ниже:


addEnd(_item)
Добавляет элемент _item в конец списка
addStart(_item)
Добавляет элемент _item в начало списка
appendList(_list)
Добавляет элементы списка _list в конец текущего списка
elements
Возвращает количество элементов в списке
getEnumerator
Возвращает указатель для извлечения элементов списка

В примере ниже создаются и объединяются 2 списка:


Результат:

Список можно инициализировать и через контейнер:



Результат:


Set (Множество)

Множества используются для хранения уникальных значений, каждый элемент множества является ключом, по которому данные автоматически сортируются при добавлении нового элемента – этим множества отличаются от списков. Множества могут содержать любые типы X++.
При добавлении в множество элемента, который уже в нем есть, ничего не произойдет - не будет никаких записей о том, сколько таких вставок было. Например, если множество типа Integer (Types::Integer) содержит только значение 7, то повторный вызов метода вставки еще одного значения 7 не даст никакого эффекта. Не важно сколько таких вставок будет сделано, множество так и будет содержать одно значение – 7.
Отсюда следуют 2 полезных варианта использования множеств:
  • хранение уникальных значений (например, RecId для избегания повторных действий над одной записью)
  • сортировка потока произвольных значений


С множествами в X++ можно работать как с математическими множествами, для этого есть операции, позволяющие получать новые множества, используя 3 метода.




Наиболее полезные методы для работы с множествами описаны в таблице ниже:


add(_item)
Добавляет элемент _item в множество
delete(_item)
Удаляет выбранный элемент _item из множества
elements
Возвращает количество элементов в множестве
getEnumerator
Возвращает enumerator для выборки из множества
in(_item)
Возвращает true, если элемент _item есть в текущем множестве
difference(_set1, _set2)
Возвращает элементы, которые есть в множестве _set1, но которых нет в множестве _set2
intersection(_set1, _set2)
Возвращает новое множество, которое содержит элементы, содержащиеся в обоих множествах _set1 и _set2
union(_set1, _set2)
Возвращает новое множество, которое содержит элементы из обоих множеств _set1 и _set2.


В примере ниже демонстрируется сортировка при заполнении множества:



Результат (нет повторяющихся значений и все элементы упорядочены):



В примере ниже демонстрируется работа с множествами X++ как с математическими множествами:



Результат:




Map (Карта соответствий)

Карта хранит пару ключ-значение. Для каждого ключа есть соответствующее значение. Ключи уникальны и все одного типа. Значения тоже все одного типа, но могут быть неуникальными. В качестве ключей и значений могут использоваться любые типы, при этом типы ключей и значений в одной карте могут отличаться. По картам быстро осуществляется поиск, что делает их полезными для кэширования данных.
Несколько ключей могут относиться к одному и тому же значению, но один ключ не может относиться к нескольким значениям. При добавлении новой пары ключ-значение в карту, где уже есть такой ключ с другим значением, связь меняется так, что ключ будет указывать на новое значение (значение у старого ключа обновится).

Наиболее полезные методы для работы с множествами описаны в таблице ниже:


delete(_key)
Удаляет ключ _key (и, соответственно, связанное значение) из карты
elements
Возвращает количество пар ключ-элемент в карте
exists(_key)
Возвращает true, если ключ _key есть в карте
insert(_key, _value)
Вставляет пару ключ-значение, где ключ - _key, а значение -  _value. Другими словами, поиски по ключу _key вернут значение _value. Если ключ _key уже есть в карте, то связанное с этим ключом значение обновится, так что новым значением для этого ключа станет _value.
lookup(_key)
Возвращает значение, связанное с ключом _key.

Важно: вызов метода lookup для ключа, которого нет в карте выдаст ошибку. Таким образом, следует сначала вызывать метод exists для проверки существования ключа.
Класс MapEnumerator используется для извлечений данных из карты. Для извлечения ключа и значения из объекта MapEnumerator используются методы currentKey() и currentValue().

Ниже пример работы с картами:



Результат:



Struct (Структура)

В структурах данные группируются в единое целое. Использоваться могут все типы из Types.
Класс Struct – один из самых простых классов коллекций. Использование структур похоже на использование контейнеров, которые имеют небольшое количество записей на конкретных позициях. Основное отличие от контейнеров в том, что структуры – это класс, а контейнеры – встроенный в X++ тип данных. В структурах используются строковые индексы, в то время как в контейнерах используются номерные индексы.
Основное преимущество структур – возможность динамического добавления новых элементов, без необходимости создавать новый тип в AOT.

Пример использования класса Struct:


Результат:



Array (Массив)

Объекты Array могут содержать элементы любого типа, в отличие от встроенного типа данных «массив» в X++. Значения в Array хранятся последовательно. Array можно расширить по необходимости, поэтому не нужно определять его размер при создании объекта. Также как и у массивов в X++ нумерация в Array начинается с единицы, а не нуля.

Пример использования Array:



Результат:






Помимо описанных классов есть еще пара не самых популярных: Stack и StackBase.

Stack (Стек)

В стеке способ организации данных построен по принципу LIFO (last in first out, последним пришел - первым ушел). Элемент добавляется вызовом метода push, а удаляется методом pop. Класс Stack в Dynamics AX может хранить только экземпляры контейнеров, соответственно, данные в стеке могут быть такого же типа, как и в контейнерах.

Пример использования стека:



Результат:



StackBase (Базовый стек)

У класса StackBase, в отличие от Stack, нет ограничения на хранение только экземпляров контейнеров. Класс StackBase предоставляет ту же функциональность, как и Stack с возможностью хранить данные любого типа, включая объекты.

Пример использования StackBase:



Результат:




На этом, пожалуй, описание классов коллекций завершу.

Happy DAXing!