列舉

一、列舉型別(Enumerated Type)

Pascal 的列舉屬於封裝性極佳的不透明型別,在實務開發中的使用頻率遠高於其他程式語言的列舉。

基礎定義寫法

type
  TAnimalKind = (akDuck, akCat, akDog);

開發規範:列舉常數名稱需加上型別縮寫雙字母前綴

  • ak = Animal Kind(TAnimalKind 的縮寫)
  • 用途:列舉常數預設歸屬於單元全域命名空間,加上前綴能大幅降低與其他變數、自定型別名稱衝突的機率。

TAnimalKind 開頭的 T 語法上可省略,僅是業界通用規範,建議保留。一眼就能辨識這是一個列舉型別。

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)

列舉

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *