8-800-700-15-02

Звонок по России
бесплатный

info@i-neti.ru

Новые возможности X++ в AX 7

asd
Дата публикации: 30.05.2016

X++ не сильно изменился с тех пор, как я начал работать с ним 11 лет назад, если не считать такие исключения из правила, как обработчики событий в AX 2012.

Но это не касается AX 7, т.к. тут уже много новых конструкций языка (и много конструкций ещё ожидается). Если вам знакомы современные объектно-ориентированные языки программирования, особенно C#, то эти новые конструкции не покажутся вам чем-то новым, но некоторые идеи будут принципиально новыми для многих разработчиков AX.

В целом, X++ теперь использует множество функций, поддерживаемых CIL, и синтаксис новых конструкций соответствует синтаксису C#, что полезно для всех, кто знает или собирается изучать C#. Чтобы использовать язык правильно, нужно остерегаться некоторых особенностей среды выполнения (CLR), таких как сбор мусора. Вы можете найти больше информации, написанной для разработчиков C#, чем для разработчиков X++, следовательно, рассматривайте изучение общих приёмов из ресурсов, изначально предназначенных для C#.

Некоторые новые особенности помогают реализовать сокрытие и разбиение приложения на логические модули, чего действительно не хватает в AX сейчас (AX7, на самом деле, не решает эту проблему полностью, это всего лишь ещё один шаг в верном направлении).

Я не буду описывать каждую новую особенность, потому что вы сами можете почитать о них здесь: AX7 wiki (Using X++ and debugger features) и mfp’s blog: (What is new in X++ in AX7). Я хочу лишь упомянуть некоторые вещи, которые я считаю особенно важными или запутанными.

Частные переменные

Это нововведение может и не выглядит очень воодушевляющим, но оно решает важную проблему. То, что все переменные имеют модификатор доступа protected (как в предыдущих версиях AX), означает, что любой класс-наследник имеет доступ  на изменение значения любой переменной классов-родителей. В классах часто есть переменные, которые нужны для их собственной логики и не должны быть открытыми ни для каких других классов.

Установка модификатора доступа переменных в значение private защищает их от непредвиденных изменений, а также упрощает разработчикам реализацию наследования класса. Например, если родительский класс содержит 8 переменных с модификатором private и 2 переменных с модификатором  protected, то разработчики при создании классов-наследников будут иметь доступ только к 2 переменным, а не ко всем сразу. Будет очевидно, что переменные с модификатором private не для них.

Константы и переменные типа readonly

Константа Const представляет собой значение, которое не меняется. Это очень простой концепт, но очень удобный для X++, в отличие от использования переменных (которые не предотвратят изменение) или макросов (которые не имеют типа и очень ущербные).

Интереснее переменные Readonly , потому что им можно установить значение в момент создания объекта и не будет возможности их изменения в дальнейшем. Это вполне распространенный подход, но в предыдущих версиях AX подобного не было. Также это позволяет создавать неизменяемые объекты.

Статические переменные

Статические переменные содержат определенные значения во всех объектах класса.  Это полезно в некоторых случаях, но также очень опасно по некоторым причинам. Пожалуйста, старайтесь избегать их использования, особенно если вы не понимаете что подразумевают эти два тезиса:

  • Статические поля часто являются причиной проблем с распределением памяти. Если статическое поле ссылается на объект-дерево с большим количеством данных, то такая ссылка делает все соответствующие объекты недоступными для сборки мусора. Если вам нужно создать такую ссылку, то слабая ссылка может помочь в некоторых таких случаях.
  • Если поле используется более чем в одном потоке, его значение может быть изменено где угодно (если только вы не реализовали какой-либо тип блокировки). Если вы установили значение статическому полю секунду назад, вы не можете быть уверенными, что какой-то другой поток не изменил значение поля. Это может привести к тяжело воспроизводимым багам. Проблемы многопоточности не ограничены использованием статических полей, но часто связаны с этим, т.к. одно и то же значение является общим для всех потоков.

Блок Finally

Блок Finally может быть использован после блока try аналогично, как и блок catch. Код, написанный внутри блока finally, будет выполнен независимо от того, выполнился код внутри блока try корректно или же он вызвал ошибку. Обычно блок используется для очистки объектов, используемых в блоке try.

Обратите внимание, что абсолютно корректно использовать блоки try/finally без блока catch (потому что, например, у вас прописана обработка исключений на более высоком уровне или же вы просто не можете обработать данное исключение).

try

{

    throw error("It's broken!");

}

finally

{

    info("Let's clean up everything");

}

Конструкция Using

Эта конструкция, на самом деле, является сокращением конструкции try/finally для классов, реализующих интерфейс IDisposable. Она предназначена для освобождения ресурсов, которыми CLR не может управлять автоматически и которые должны быть освобождены при первой возможности (например, файловый дескриптор). Вы хотите быть уверенными, что, независимо ни от чего (например, выброшено исключение), не оставите ресурс заблокированным, память занятой и т.д. Если вы пишете класс-обертку ресурса, корректно реализуя интерфейс IDisposable, и вы создаете объект класса с использованием конструкции using, то вы в безопасности.

Вы часто встретите конструкцию dispose, если начнете использовать .NET типы для потоков, запросов БД и сетевых подключений. Убедитесь, что вы корректно уничтожаете все уничтожаемые объекты, иначе у вас, к примеру, могут закончиться  свободные подключения к БД. Конструкция using облегчит вам разработку.

Возможно, вам никогда не требовалось ничего подобное раньше, но, я думаю, мы будем путать код X++ и код .NET гораздо чаще в AX 7, потому что теперь это гораздо проще, следовательно, многим разработчикам будет полезно это знание.

Обратите внимание, что вы можете использовать конструкцию using для других целей – всё, что вам требуется, это реализовать интерфейс IDisposable. Следующий пример иллюстрирует класс TimeMeasure для измерения времени внутри конструкции using.

using (TimeMeasure m = new TimeMeasure())

{

    // Do something here

}

Блок using начинается с создания объекта класса. И в момент, когда блок завершается, автоматически выполняется метод Dispose.

Пожалуйста, воспринимайте этот пример как демонстрацию работы конструкции, а не как правило не использовать интерфейс IDisposable . Эта технология полезна в некоторых случаях, но они не очень распространенные.

Далее представлен код, который показывает ещё некоторые особенности AX7:

using System.Diagnostics;

 

class TimeMeasure implements System.IDisposable

{

    private Stopwatch stopwatch = Stopwatch::StartNew();

 

    public void Dispose()

    {

        stopwatch.Stop();

        info(stopwatch.Elapsed.ToString());

    }

}

Обратите внимание, что мой код выше использует конструкцию using в другом контексте: using System.Diagnostics. Это совершенно не связанные конструкции, просто используется то же слово.

Методы расширения

Методы расширения  предоставляют способ сэмулировать, как будто вы добавили метод в класс, таблицу, представление, мэп без фактического их изменения.

Допустим, я хочу создать метод для конвертации переменной списка в сет. Я не могу изменить класс List (потому что он определен в ядре AX), но я легко могу написать статический метод, который реализует эту конвертацию.

Set set = List_Extension::toSet(list);

Можно сделать так, но этот код не так уж легко читается, и вы должны знать о существовании класса Extension. Я бы предпочел сделать так:

Set set = list.toSet();

И это как раз то, что позволяют реализовать методы расширения.

Чтобы метод считался методом расширения (и соответствующий класс, где метод определен), он должен удовлетворять определенным условиям, которые вы можете найти в вики AX7. Лучше я вам продемонстрирую реализацию метода toSet():

public static class List_Extension

{

    public static Set toSet(List _list)

    {

        //TODO: throw error is _list is null

 

        Set set = new Set(_list.typeId());

        ListEnumerator enumerator = _list.getEnumerator();

        while (enumerator.moveNext())

        {

            set.add(enumerator.current());

        }

 

        return set;

    }

}

Обратите внимание, что это всего лишь статический метод, который получает список как параметр. Он не может получить доступ к ни к какому не-public элементу класса, как если бы метод был на самом деле добавлен в класс.

Вам не обязательно создавать какие-либо методы расширения самим, но вы хотя бы должны знать об их существовании. Иначе вы можете долго гадать, откуда взялся тот или иной метод.

Это классные дополнения X++, не так ли? И не забывайте, что это не исчерпывающий список нововведений.

 

Автор: Martin Dráb, Works for Freelancer Czech Republic

 

Акция "Тест-драйв Сопровождения"

Попробуй сопровождение АХ до подписания договора!


Узнать подробнее

Другие записи в блоге

20.02.2020
Упрощенное получение значения аналитики После релиза Dynamics 365 for Operations [Enterprise Edition] структура аналитик и бизнес-логика немного изменились. В этом посте я познакомлю вас с...
17.02.2020
В этом ролике Дмитрий, разработчик Dynamics расскажет:
24.01.2020
В этом видео Дмитрий Уткин расскажет о том, как автоматически собрать модель в Microsoft Dynamics AX 2012 с помощью Pipelines в Azure DevOps.

Подпишитесь на блог

Все интересные статьи нашего блога на Вашем почтовом ящике!


Подписка

Служба контроля качества сервиса

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


Письмо директору