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

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

Для того чтобы описать собственное перечисление в коде MQL5 используется ключевое слово enum. Простейшая форма описания имеет следующий вид:

enum name
{
  element1,
  element2,
  element3
};

Такое определение регистрирует в программе тип перечисления с именем name и указанными в фигурных скобках, через запятую, элементами (их количество ограничено лишь максимальным значением int, что для практических задач можно расценивать как отсутствие ограничений). Идентификаторы element1, element2, element3 можно далее использовать в программе в пределах того контекста, где произведено определение: глобально (т.е. снаружи всех функций) или внутри какой-либо функции (см. раздел Контекст, область видимости и время жизни переменных).

Обратите внимание на точку с запятой после закрывающей скобки. Она нужна, потому что описание перечисления — это отдельная инструкция, а после любой инструкции языка MQL5 необходимо ставить точку с запятой.

Идентификаторы получают по умолчанию константные значения, начиная с 0, каждый следующий — на 1 больше предыдущего. При необходимости программист может задать для каждого элемента конкретное значение, после знака '=' справа от идентификатора. Например, вышеприведенная запись эквивалентна следующей:

enum name
{
  element1 = 0,
  element2 = 1,
  element3 = 2
};

В качестве значений допускается указывать только константы или выражения, которые компилятор способен вычислить на стадии компиляции (подробнее об этом — в примере ниже).

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

enum name
{
  element1 = 1,
  element2,
  element3 = 10,
  element4,
  element5
};

Здесь первые два элемента получают значения 1 и 2 (вычислено), а начиная с третьего — 10 (указано явно), 11 и 12 (два последних вычислены от 10).

В скрипте TypeUserEnum.mq5 приведено несколько примеров описания пользовательских перечислений.

const int zero = 0// runtime value is not known at compile time
 
enum
{
  MILLION = 1000000
};
 
enum RISK
{
  // OFF      = zero, // error: constant expression required
  LOW      = -1,
  MODERATE = -2,
  HIGH     = -3,
};
 
enum INCOME
{
  LOW      = 1,
  MODERATE = 2,
  HIGH     = 3,
  ENORMOUS = MILLION,
};
 
void OnStart()
{
  enum INTERNAL
  {
    ON,
    OFF,
  };
 
  // int x = LOW; // ambiguous access, can be one of
  int x = RISK::LOW;
  int y = INCOME::LOW;
}

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

Перечисление RISK показывает, что можно назначать элементам отрицательные значения. Закомментированный элемент OFF не может быть описан из-за попытки его инициализации неконстантным выражением: в данном случае там указана переменная zero, значение которой компилятор не способен вычислить.

В перечислении INCOME элемент ENORMOUS успешно инициализируется значением из элемента MILLION другого перечисления, определенного выше. Перечисления создаются в момент компиляции и потому доступны в выражениях инициализации.

Перечисление с MILLION не имеет имени — такие перечисления называются анонимными. Их основное применение — декларация констант. Однако чаще для констант все же используются именованные перечисления, так как они позволяют группировать элементы по смыслу.

Поскольку в примере определено 2 перечисления с элементами, имеющими одинаковые имена, указание идентификатора LOW при объявлении переменной x приводит к ошибке компиляции "неоднозначный доступ" ("ambiguous access") — не понятно, элемент какого перечисления имеется в виду. Следует учесть, что идентификаторы могут иметь (и имеют в данном случае) разные значения.

Для разрешения этой проблемы существует специальный оператор контекста — два двоеточия "::". С помощью него формируется полный идентификатор элемента языка, в нашем случае элемента перечисления: сперва указывается имя перечисления, далее оператор "::" и затем идентификатор элемента. Например, RISK::LOW и INCOME::LOW. Позднее мы познакомимся со всеми операторами в соответствующем разделе.