AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX: База знаний и проекты
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 20.04.2009, 16:58   #1  
petergunn is offline
petergunn
Участник
 
118 / 274 (10) ++++++
Регистрация: 30.08.2005
Адрес: Tyumen
Post Классы коллекций (инициализация, сериализация): List, Set, Map.
Некоторое время назад (после выхода Dynamics Ax 4.0) набросал для себя заметки о структуре упакованных в контейнер данных возвращаемых методом set.pack(). Недавно 'нашел' эти заметки, кое-что из них перепроверил для AX2009 (RTM), + добавил информацию и примеры для list и map. Вот что получилось в итоге:

Просматривая штатную документацию по классам коллекций (List, Set, Map - далее по тексту Collections) в Ах обратил внимание на описание структуры контейнеров используемых для сериализации в методах collections.pack() и Collections::create(container)

List Class
Цитата:
The container created by this method contains 3 elements before the first element from the list:
- A version number for the container
- An integer that identifies the data type of the list elements
- The number of elements in the list
Set Class
Цитата:
The container created by this method contains 3 elements before the first element from the set:
- A version number for the container
- An integer that identifies the data type of the set elements
- The number of elements in the set
Map Class
Цитата:
The container created by this method contains 4 elements before the first element from the map:
- A version number for the container
- An integer that identifies the data type of the keys in the map
- An integer that identifies the data type of the values in the map
- The number of elements in the map
т.е. структура упакованных объектов в контейнер примерно идентична и состоит из 2 частей: заголовок и непосредственно сами данные - [packedContainer] = [<header>] + [<elements>]
где [<header>] можно представить как:
  • [ <CurrentVersion>(integer), <тип элемента - elementTypeId>(integer), <число упакованных элементов - elementCount>(integer)] для List и Set
  • [ <CurrentVersion>(integer), <тип ключа - keyTypeId>(integer), <тип значения - elementTypeId>(integer), <число упакованных элементов - elementCount>(integer)] для Map
(значения keyTypeId и elementTypeId - числовые коды перечисления (base enum) Types используемые при создании экземпляров классов коллекций).
Проверка кода:
X++:
    info( strfmt( "List packed version: %1", conpeek( new List( Types::Integer ).pack(), 1 ) ) ) ;
    info( strfmt( "Set packed version: %1", conpeek( new Set( Types::Integer ).pack(), 1 ) ) ) ;
    info( strfmt( "Map packed version: %1", conpeek( new Map( Types::Integer, Types::Integer ).pack(), 1 ) ) ) ;
на приложениях Axapta 3.0 SP5, Dynamics Ax 4.0 SP2, Dynamics AX2009 выдала идентичные результаты:
Цитата:
List packed version: 1
Set packed version: 1
Map packed version: 1
P.S. Понятно, что с изменением алгоритма(структуры) упаковки данных, номер версии контейнера может измениться.

и где [<elements>]
  • [ element1, element2, ..., elementN ] для List и Set
  • [ key1, value1, key2, value2,..., keyN, valueN ] - пары значений element(key, value) для Map

Для типа элементов Types::Class классов коллекций Collections секция [<elements>] хранит значения в виде пар <classId, [packed class data]>: <element_N> = <classId_N, [packed class data_N]>
X++:
static void jbListClasses(Args _args)
{
    Set     setDemo = new Set( Types::Date ) ;
    Map     mapDemo = new Map( Types::Integer, Types::String) ;
    List    listOfClasses = new List( Types::Class ) ;
 
    void showContainerInfo( container _con )
    {
        str valueAsString   ;
        str totalAsString   ;
        int iCount = conlen( _con ) ;
        int idx ;
 
        for( idx=1; idx<= iCount; idx++ )
        {
            if( typeof( conpeek( _con, idx ) ) != Types::Container )
                valueAsString = strfmt( "%1", conpeek( _con, idx ) ) ;
            else valueAsString = strfmt( "[%1]", con2str( conpeek( _con, idx ) ) ) ;

            if( totalAsString )
                totalAsString += ';' ;
            totalAsString += valueAsString ;
            
            info( strfmt( "position: %1, value: %2", idx, valueAsString ) );
        }
        info( strfmt( "[%1]", totalAsString ) ) ;
    }
    ;

    setDemo.add( 01\01\2009 ) ;
    setDemo.add( systemdateget() ) ;

    mapDemo.insert( 1, 'one' ) ;
    mapDemo.insert( 2, 'two' ) ;
    
    listOfClasses.addStart( new List( Types::Integer ) ) ;
    listOfClasses.addEnd( setDemo ) ;
    listOfClasses.addEnd( mapDemo ) ;
    listOfClasses.addEnd( SalesTable2LineField::construct( fieldNum( SalesTable, Dimension ) ) ) ;
    
    showContainerInfo( listOfClasses.pack() ) ;
}
Результат для Dynamics Ax 4.0 (скобками '{' '}' и подчеркиванием отмечены блоки <header> и <elements> контейнеров с упакованными данными):
Цитата:
position: 1, value: 1
// <header> listOfClasses - version
position: 2, value: 10
// <header> listOfClasses - Types::Class
position: 3, value: 4
// <header> listOfClasses - listOfClasses.elements()
position: 4, value: 65231
// <elements> listOfClasses[1,1] - classNum( List )
position: 5, value: [1,1,0]
// <elements> listOfClasses[1,2] - List.pack()
position: 6, value: 65238
// <elements> listOfClasses[2,1] - classNum( Set )
position: 7, value: [1,3,2,2009.01.01,2009.04.17]
// <elements> listOfClasses[2,2] - Set.pack() : [{1,3,2},{2009.01.01},{2009.04.17}]
position: 8, value: 65236
// <elements> listOfClasses[3,1] - classNum( Map )
position: 9, value: [1,1,0,2,1,one,2,two]
// <elements> listOfClasses[3,2] - Map.pack() : [{1,1,0,2},{1,one},{2,two}]
position: 10, value: 4512
// <elements> listOfClasses[4,1] - classNum( SalesTable2LineField )
position: 11, value: [1,23,0]
// <elements> listOfClasses[4,2] - SalesTable2LineField.pack()
[1;10;4;65231;[1,1,0];65238;[1,3,2,2009.01.01,2009.04.17];65236;[1,1,0,2,1,one,2,two];4512;[1,23,0]]
// [{1;10;4};{65231;[1,1,0]};{65238;[1,3,2,2009.01.01,2009.04.17]};{65236;[1,1,0,2,1,one,2,two]};{4512;[1,23,0]}]
Для успешной сериализации объектов классов коллекций для типа элементов Types::Class необходимо придерживаться Pack-Unpack Design Pattern для классов помещаемых в коллекцию.

Зная структуру упакованного состояния класса коллекции можно используя Collections::create() эмулировать объявление экземпляра класса коллекции с инициализацией значений (передавая в качестве параметра сформированыый контейнер). Тут стоит оговориться, что при изменении разработчиками структуры упаковки состояния классов Collections ниже приведенные примеры скорее всего не будут работать.

На примере элементарных(встроенных) типах данных в X++, код:
X++:
    Set setDemo = new Set( Types::String) ;
    ;
    setDemo.add( 'Mum' ) ;
    setDemo.add( 'washed' ) ;
    setDemo.add( 'a' ) ;
    setDemo.add( 'frame') ;
можно заменить объявлением:
X++:
    Set setDemo = Set::create( [ 1, any2int(Types::String), 4 ] + [ 'Mum', 'washed', 'a', 'frame' ] ) ;
    // Set setDemo = Set::create( [ 1, 0, 4, 'Mum', 'washed', 'a', 'frame' ] ) ; // Types::String = 0
Пример job'а для Set:
X++:
static void jbSetInitDemo(Args _args)
{
    Set setByInit = Set::create( [ 1, any2int(Types::String), 4 ] + [ 'Mum', 'washed', 'a', 'frame' ] ) ;
    Set setByCode = new Set( Types::String ) ;
 
    void showSet( Set _set, TempStr _prefix = '' )
    {
        SetEnumerator   setEnumerator = _set.getEnumerator() ;
        ;
        setPrefix( _prefix ) ;
        while( setEnumerator.moveNext() )
            info( strfmt( "%1", setEnumerator.current() ) ) ;
    }
    ;

    setByCode.add( 'Mum' ) ;
    setByCode.add( 'washed' ) ;
    setByCode.add( 'a' ) ;
    setByCode.add( 'frame') ;

    info( 'set elements' ) ;
    showSet( setByCode, 'inserted by code' ) ;
    showSet( setByInit, 'inserted by create' ) ;
}
и List:
X++:
static void jbListInitDemo(Args _args)
{
    List listByInit = List::create( [ 1, any2int(Types::String), 4 ] + [ 'Mum', 'washed', 'a', 'frame' ] ) ;
    List listByCode = new List( Types::String ) ;
 
    void showList( List _list, TempStr _prefix = '' )
    {
        ListEnumerator  listEnumerator = _list.getEnumerator() ;
        ;
        setPrefix( _prefix ) ;
        while( listEnumerator.moveNext() )
            info( strfmt( "%1", listEnumerator.current() ) ) ;
    }
    ;

    listByCode.addStart( 'Mum' ) ;
    listByCode.addEnd( 'washed' ) ;
    listByCode.addEnd( 'a' ) ;
    listByCode.addEnd( 'frame') ;

    info( 'list elements' ) ;
    showList( listByCode, 'inserted by code' ) ;
    showList( listByInit, 'inserted by create' ) ;
}
В случае контейнерных элементов:
X++:
    Set setDemo = new Set( Types::Container ) ;
    ;
    setDemo.add( [ 'a', 'b', 'c', 'd' ] ) ;
    setDemo.add( [ 'e', 'f', 'g', 'h' ] ) ;
    setDemo.add( [ 1, 2, 3, 4 ] ) ;
можно заменить 'эквивалентным' кодом :
X++:
    Set setDemo = Set::create( [ 1, any2int(Types::Container), 3 ] + [ [ 'a', 'b', 'c', 'd' ], [ 'e', 'f', 'g', 'h' ], [ 1, 2, 3, 4 ] ] ) ;
Пример инициализации Map'а программно :
X++:
    Map mapDemo = new Map( Types::Integer, Types::Container ) ;
    ;
    mapDemo.insert( 1, [ 'one', 'single' ] ) ;
    mapDemo.insert( 2, [ 'two', 'double' ] ) ;
    mapDemo.insert( 3, [ 123, 456 ] ) ;
    mapDemo.insert( 4, [ ABC::C ] ) ;
и его 'аналог':
X++:
    Map mapDemo = Map::create( [ 1, any2int(Types::Integer), any2int(Types::Container), 4 ]
                                + [ 1, [ 'one', 'single' ] ]
                                + [ 2, [ 'two', 'double' ] ]
                                + [ 3, [ 123, 456 ] ]
                                + [ 4, [ ABC::C ] ] ) ;
X++:
static void jbMapInitDemo(Args _args)
{
    Map mapByCode = new Map( Types::Integer, Types::Container ) ;
    Map mapByInit = Map::create( [ 1, any2int(Types::Integer), any2int(Types::Container), 4 ]
                                    + [ 1, [ 'one', 'single' ] ]
                                    + [ 2, [ 'two', 'double' ] ]
                                    + [ 3, [ 123, 456 ] ]
                                    + [ 4, [ 123.456 ] ] ) ;
 
    void showMap( Map _map, TempStr _prefix = '' )
    {
        MapEnumerator   mapEnumerator = _map.getEnumerator() ;
        ;
        setPrefix( _prefix ) ;
        while( mapEnumerator.moveNext() )
            info( strfmt( "key: %1, value: [%2]", mapEnumerator.currentKey(), con2str( mapEnumerator.currentValue() ) ) );
    }
    ;

    mapByCode.insert( 1, [ 'one', 'single' ] ) ;
    mapByCode.insert( 2, [ 'two', 'double' ] ) ;
    mapByCode.insert( 3, [ 123, 456 ] ) ;
    mapByCode.insert( 4, [ 123.456 ] ) ;
    
    info( 'map elements' ) ;
    showMap( mapByCode, 'inserted by code' ) ;
    showMap( mapByInit, 'inserted by create' ) ;
}
За это сообщение автора поблагодарили: mazzy (5), sukhanchik (10), Logger (8), gl00mie (10), Gustav (5), Jorj (1), plumbum (1), in.dc (2).
Теги
container, faq, list, map, set, классы коллекций, полезное, pack, unpack

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
Kashperuk Ivan: List panels in Dynaics AX - a short description of SysListPanel class Blog bot DAX Blogs 1 21.10.2007 22:51
dax-dilettante: 15. System Classes \ The Collection Classes Blog bot DAX Blogs 0 26.09.2007 23:51
casperkamal: Creating a Custom Display List - Blog bot DAX Blogs 0 27.12.2006 17:20
Инициализация map axaLearner DAX: Программирование 3 24.08.2004 15:41

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 20:15.