Перечислимый тип

1. Перечислимые типы (Enumerated Type)

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

Базовый синтаксис объявления

type
  TAnimalKind = (akDuck, akCat, akDog);

Соглашение по именованию: для констант перечисления используется двухбуквенный префикс, сокращение названия типа

  • ak = Animal Kind (сокращение от TAnimalKind)
  • Значение: по умолчанию константы перечисления находятся в глобальном пространстве имен модуля. Префикс сильно снижает риск конфликтов имен с переменными и пользовательскими типами.

Буква T в начале TAnimalKind синтаксически необязательна, это просто стандарт индустрии. Рекомендуется оставлять её, чтобы сразу понять, что это перечислимый тип.

type
  AnimalKind = (akDuck, akCat, akDog); // Без префикса T, синтаксис корректенCode language: JavaScript (javascript)

Дополнительные замечания:

  1. Одинаковые идентификаторы не вызывают немедленной ошибки компиляции; разные модули могут содержать одинаковые имена;
  2. Тем не менее для читаемости кода и удобства глобального поиска (grep) стоит самостоятельно избегать конфликтов имен.

Перечисления с ограниченной областью видимости ({$scopedenums on})

Директива компилятора {$scopedenums on} помещает константы перечисления внутрь родительского типа, не загрязняя глобальное пространство имен

По умолчанию выключено
{$ifdef FPC} {$mode objfpc} {$H+} {$endif}
{$codepage utf8}
program EnumDemo;

type TAnimalKind = (akDuck, akCat);

var k: TAnimalKind;

begin
  k := akDuck; // Константа используется напрямую, без префикса TAnimalKind.
  WriteLn(k); //Выводится akDuck
end.Code language: PHP (php)

Как мы говорили раньше, выполнение кода начинается с ключевого слова begin.

Использовать префикс ak — хорошая практика для предотвращения конфликтов, но даже без него ошибок не будет.

{$ifdef FPC} {$mode objfpc} {$H+} {$endif}
{$codepage utf8}

program EnumDemo;

type TAnimalKind = (Duck, Cat);

var k: TAnimalKind;

begin
  k := Duck; 
  WriteLn(k);//Duck
end.Code language: PHP (php)

При включенном {$scopedenums on} обязательно указывать «ИмяТипа.»

  1. При обращении нужен квалификатор типа: TAnimalKind.akDuck
  2. Префикс ak больше не нужен, константы можно писать просто Duck, Cat, Dog
{$scopedenums on}
type
  TAnimalKind = (Duck, Cat, Dog);
var
  Kind: TAnimalKind;
begin
  Kind := TAnimalKind.Duck; // Квалификатор типа обязателен
end;Code language: PHP (php)

Если не добавлять квалификатор типа

{$ifdef FPC} {$mode objfpc} {$H+} {$endif}
{$codepage utf8}
{$scopedenums on}
program EnumDemo;

type TAnimalKind = (Duck, Cat);
var k: TAnimalKind;
begin
  k := Duck; // Отсутствует TAnimalKind.
  WriteLn(k);
end.Code language: PHP (php)

Появится ошибка компиляции

D:\fpcdemo>fpc mydemo.dpr
Free Pascal Compiler version 3.2.2 [2021/05/15] for i386
Copyright (c) 1993-2021 by Florian Klaempfl and others
Target OS: Win32 for i386
Compiling mydemo.dpr
mydemo.dpr(9,8) Error: Identifier not found "Duck"
mydemo.dpr(11,4) Fatal: There were 1 errors compiling module, stopping
Fatal: Compilation aborted
Error: C:\FPC\3.2.2\bin\i386-Win32\ppc386.exe returned an error exitcodeCode language: CSS (css)

Если добавить квалификатор типа

{$ifdef FPC} {$mode objfpc} {$H+} {$endif}
{$codepage utf8}
{$scopedenums on}
program EnumDemo;

type TAnimalKind = (Duck, Cat);
var k: TAnimalKind;
begin
  k := TAnimalKind.Duck; // Полный квалификатор типа
  WriteLn(k);
end.Code language: PHP (php)

Код скомпилируется без ошибок

Явное преобразование между перечислениями и целыми числами

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

  1. Перечисление → Целое число: Ord(ПеременнаяПеречисления)
  2. Целое число → Перечисление: явное преобразование TAnimalKind(ЦелоеЗначение)
  3. Перед преобразованием числа в перечисление обязательно проверяйте диапазон: допустимый интервал 0 ~ Ord(High(ТипПеречисления))
type
  TAnimalKind = (akDuck, akCat, akDog);
var
  Kind: TAnimalKind;
  Num: Integer;
begin
  Kind := akCat;
  Num := Ord(Kind); // Порядковый номер akCat равен 1, Num=1

  Num := 2;
  // Сначала проверяем диапазон, затем преобразуем
  if (Num >= 0) and (Num <= Ord(High(TAnimalKind))) then
    Kind := TAnimalKind(Num);
end;Code language: JavaScript (javascript)
{$ifdef FPC} {$mode objfpc} {$H+} {$endif}
{$codepage utf8}

program EnumDemo;

type
  TAnimalKind = (akDuck, akCat, akDog);
var
  Kind: TAnimalKind;
  Num: Integer;
begin
  Kind := akCat;
  Num := Ord(Kind); // akCat index is 1,Num=1

  Num := 2;

  if (Num >= 0) and (Num <= Ord(High(TAnimalKind))) then
    Kind := TAnimalKind(Num);

  WriteLn(Num);//2
end.Code language: PHP (php)

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *