編輯 | 究查 | 歷程 | 原始

Delphi 命名規範與風格指南 (Naming Conventions)

1. 大小寫敏感度 (Case Sensitivity)

在探討具體規範前,我們必須先釐清 Delphi 編譯器的特性:

  • Delphi 是「大小寫不敏感」的 (Case-Insensitive):

對 Delphi 編譯器而言,CaseID、caseid、CASEID 甚至 cAsEiD 都是**完全相同**的變數。這與 C++, C#, Java, JavaScript (這些是 Case-sensitive) 有著根本上的不同。

  • 為什麼還需要規範?

既然編譯器不在乎,為什麼大家還是很嚴格遵守大小寫?因為**「人」在乎**。統一的大小寫規範(通常是 PascalCase)能極大提升程式碼的可讀性。

  • 何時大小寫「絕對」重要?

當 Delphi 與外部世界溝通時!例如:

  1. 呼叫 C/C++ 寫的 DLL 函數(如 Windows API)。
  2. 處理 JSON/XML 資料解析時。
  3. 將 ActiveX/OCX 元件提供給網頁端的 JavaScript 呼叫時(這也是為什麼你在 _TLB.pas 裡會看到像 caseno 這種全小寫的屬性,因為它是為了迎合 JS/Web 的習慣)。

2. 常見的程式命名風格 (Casing Styles)

了解以下三種風格,有助於我們理解 Delphi 的選擇:

  1. PascalCase (帕斯卡命名法):每個單字的首字母都大寫。
  • 範例:CalculateTotal, UserName, CaseID
  • Delphi 的最愛:Delphi 絕大多數的命名都基於這個風格。
  1. camelCase (駱駝命名法):第一個單字全小寫,後續單字首字母大寫。
  • 範例:calculateTotal, userName, caseId
  • Delphi 的應用:偶爾用於區域變數(Local Variables),但在傳統 Delphi 中不如 Java/C# 常見。
  1. snake_case (蛇形命名法):全小寫,單字間用底線 _ 分隔。
  • 範例:calculate_total, user_name, case_id
  • Delphi 的應用:**極少使用**。通常只出現在早期的舊程式碼,或是為了與 C 語言庫(如 SQLite, LibVLC)保持一致時才會使用。

3. Delphi 核心命名規範 (前綴字慣例)

Delphi 最具特色的就是使用**單一字母前綴**來標示該元素的「種類」或「作用域」。

元素類型 (Type) 前綴 命名風格 範例與說明
類別 (Class/Type) T PascalCase TForm, TCustomer
(T 代表 Type,所有類別與自訂型別皆以此開頭)
介面 (Interface) I PascalCase IUnknown, IScanner
(I 代表 Interface)
例外 (Exception) E PascalCase EOutOfMemory, EFileNotFound
(E 代表 Exception)
指標 (Pointer) P PascalCase PChar, PInteger
(P 代表 Pointer)
私有欄位 (Field) F PascalCase FCaseID, FWidth
(F 代表 Field,僅存在於類別的 private/protected 區段)
屬性 (Property) PascalCase CaseID, Width
(對外公開的屬性,通常用來讀寫對應的 F 變數)
方法參數 (Parameter) A PascalCase AOwner, ACaseID
(A 代表 Argument,用以避免與內部 Field 撞名)

4. 變數與常數命名

  • 區域變數 (Local Variables)
  • 通常使用 PascalCase,不加前綴。例如:ImageStream, TotalCount。
  • 迴圈計數器習慣使用大寫字母:I, J, K。
  • 全域變數 (Global Variables)
  • 盡量避免使用。若必須使用,部分開發者會加上 G 前綴(如 GSystemStatus)。
  • 常數 (Constants)
  • 現代 Delphi 傾向直接使用 PascalCase 配合列舉。部分開發者使用 c 前綴(如 cMaxRetries)。

5. 列舉型別 (Enumerations) 的特殊規範

Delphi 對列舉型別有非常優雅的規範:**型別以 T 開頭,列舉值以該型別的 2~3 個字母縮寫作為小寫前綴。**

  • 範例

type

TTransMode = (tsHttp, tsFtp, tsNone);

TFormAlign = (alNone, alTop, alBottom, alLeft, alRight, alClient);

6. 檔案與單元 (File & Unit) 命名

在 Delphi 中,檔案名稱(.pas)與程式碼內宣告的單元名稱(unit XXX;)**必須完全一致**。

  • 基本規則:使用 PascalCase,避免使用空白與特殊字元。
  • 視覺化窗體 (Forms):單元檔名通常結尾加上 Frm、Form 或 UI。
  • 資料模組 (DataModules):單元檔名通常結尾加上 DM。

7. 檔案命名後綴與副檔名解析 (File Extensions & Suffixes)

在 Delphi 的專案架構中,除了單元名稱外,副檔名與特定的檔名後綴也承載了重要的架構意義。

**7.1 檔名後綴 _TLB**

當你開發或匯入 COM、ActiveX 或 Automation Server 時,Delphi IDE 會自動生成以 _TLB 結尾的檔案(例如 CB_IMGPSScan_TLB.pas)。

  • 意義:TLB 是 Type Library (類型庫) 的縮寫。
  • 作用:Type Library 本身是一個二進位檔案,用來跨語言描述 COM 元件的介面。Delphi 為了讓 Object Pascal 能呼叫這些介面,會將其反編譯成 .pas 程式碼,並自動加上 _TLB 後綴。
  • ⚠️ 核心規範:**絕對不要手動修改 _TLB.pas 檔案的內容!** 只要你在 IDE 中重新整理 Type Library,這個檔案就會被完全覆蓋重寫。若要實作功能,應寫在對應的 _Imp.pas 或其他單元中。

7.2 常見的 Delphi 副檔名

以下是 Delphi 專案中常見的副檔名及其具體用途:

副檔名 類型 說明
.pas 原始碼 (Source) Pascal Source File。最常見的程式碼檔案(單元/Unit),包含介面宣告 (interface) 與實作邏輯 (implementation)。
.dpr 專案檔 (Project) Delphi Project。專案的主進入點,定義這是一個執行檔 (program) 還是程式庫 (library),並包含專案的啟動邏輯與引入單元列表。
.dfm 介面檔 (Form) Delphi Form。儲存視覺化窗體 (Form) 或資料模組 (DataModule) 上所有元件的屬性與佈局設定。一定與同名的 .pas 檔成對出現。
.dcu 編譯檔 (Compiled) Delphi Compiled Unit。.pas 檔案成功編譯後產生的二進位結果。最終連結器 (Linker) 會將這些 .dcu 打包成 EXE 或 DLL。
.res 資源檔 (Resource) Compiled Resource File。二進位資源檔,包含應用程式圖示 (Icon)、版本資訊或夾帶的檔案。通常在 .dpr 中用 {$R *.res} 編譯指令引入。 |
| .tlb | 類型庫 (Type Lib) | Type Library。微軟 COM 標準的二進位檔案,用來描述 ActiveX 元件對外公開的介面、方法與屬性,供其他語言(如 C++, JS)參考呼叫。 |
| .ridl | 介面定義 (RIDL) | Restricted Interface Definition Language。較新版 Delphi 用來取代舊式二進位 .tlb 的純文字介面定義檔。開發者可以透過文字或 IDE 編輯它,編譯時會自動生成對應的 .tlb 與 _TLB.pas。 |
| .ocx | 控件擴充檔 | OLE Control Extension。本質上是一個特殊格式的 DLL(動態連結函式庫),內部包含了 ActiveX 控件。Windows 系統可以透過 regsvr32 註冊它,讓 IE 瀏覽器或其他應用程式嵌入使用。 |

8. Delphi 架構與現代語言概念對應 (Architecture Mapping)

對於習慣 C#、Java、Python 或 JavaScript 的開發者,了解 Delphi 專屬的結構名詞如何對應到現代語言是非常重要的。

8.1 Namespace (命名空間)

  • 現代語言對應:等同於 C# 的 namespace、Java 的 package,或是 C++ 的 namespace,用來組織程式碼並避免名稱衝突。
  • Delphi 的對應
  • 早期 Delphi (如 Delphi 7/2007):沒有明確的 Namespace 關鍵字,**unit (單元) 本身就充當了扁平化的命名空間**。為避免撞名,開發者強烈依賴「前綴字」(例如 Indy 網路套件都以 Id 開頭,如 IdHTTP;Envision 影像套件以 En 開頭,如 EnScan)。
  • 現代 Delphi (XE2 以後):正式引入了 Dotted Unit Names (點綴命名) 作為 Namespace 的支援。例如原先的 SysUtils.pas 變成了 System.SysUtils.pas,Forms.pas 變成了 Vcl.Forms.pas。

8.2 Class (類別)

  • 現代語言對應:等同於 C# / Java / TS 的 class。
  • Delphi 的對應:同樣使用 class 關鍵字,但在 Delphi 中必須宣告於 type 區塊之下。如前文所述,類別名稱慣例上**必須以大寫 T 開頭**(例如 TMainForm)。實體化時,Delphi 是呼叫建構子方法(如 TMyClass.Create),而不是使用 new 關鍵字。

8.3 Unit (單元, .pas)

  • 現代語言對應:等同於 ES6 的 Module (.js/.ts)、Python 的 Module (.py),或是 C/C++ 中 .h (標頭檔) 與 .cpp (原始檔) 的綜合體。
  • Delphi 的對應:unit 是 Delphi 程式碼封裝的最核心基礎。一個完整的 unit 會被劃分為兩個主要區塊:
  • interface (介面區):等同於對外輸出的 API 或 Header,這裡宣告的型別與函式可透過 uses 關鍵字被其他檔案呼叫。
  • implementation (實作區):等同於內部的私有邏輯實作。在這裡宣告的變數或函式,對外部檔案是完全隱藏的。

8.4 Library (程式庫, .dpr)

  • 現代語言對應:等同於 C# 的 Class Library 專案、Node.js 的原生 C++ 附加元件,或 C/C++ 的 Shared Library / 動態連結檔
  • Delphi 的對應:在專案原始檔中(.dpr),如果開頭宣告為 library(而非一般視窗程式的 program),代表它的編譯目標是一個不具獨立執行程序的主體,通常會編譯為 .dll.ocx。這類專案主要透過 exports 關鍵字,將內部的函式暴露給作業系統或其他語言的程式(如 C++ 或網頁瀏覽器)來呼叫。

9 Compiler Directive

在 Delphi (Object Pascal) 中,{ } 符號的意義取決於緊跟在 { 後面的第一個字元。這是一個非常基礎但關鍵的語法,讓我們來拆解你的疑問:


9.1 { } 符號的基本意義:註解 (Comments)

在最基本的情況下,{ }「區塊註解」。編譯器會完全忽略裡面的內容。

  • { TCB_IMGPSScanX }:這是一段 註解
    • 作用:通常開發者寫這行是為了告訴讀程式碼的人:「接下來這一段程式碼是屬於 TCB_IMGPSScanX 類別的實作」。它對程式執行**沒有任何影響**。
  • TCB_IMGPSScanX:這是一段 程式碼 (識別碼)
    • 作用:編譯器會去尋找名為 TCB_IMGPSScanX 的類別、型別或變數。如果沒定義,編譯就會報錯。

9.2 {$ } 符號的進階意義:編譯指令 (Compiler Directives)

{ 後面緊跟著 $ 字元時,它就從「註解」變成了 「編譯指令」。這不是給人看的,而是**給編譯器下的指令**。

關於 {$I filename} (Include 指令)

你看到的 {$I ...}Include Directive

  • 作用:它的功能就像是編譯器的「複製貼上」工具。它告訴編譯器:「在編譯這行時,請先去把 CB_IMGPSScanImp_UI.pas 檔案裡的文字**原封不動地抓過來,塞到這個位置**,然後再繼續往下編譯」。
  • 目的:這通常用來處理非常巨大的程式碼檔案。就像你之前的 CB_IMGPSScanImp.pas 檔案太長了,開發者把它拆成小檔案,最後用這種方式在主檔「匯合」。

9.3 解析你的範例語法

你提到的這一段:
{$I CB_IMGPSScanImp_UI.pas}{$I CB_IMGPSScanImp_Scan.pas}...initialization

這是在做什麼?

這既不是定義,也不是宣告,更不是字串。它是一種 「程式碼組合行為」

  1. 組合實作內容:編譯器會依序讀入 UI.pasScan.pasData.pasUtils.pas。這四個檔案裡放的通常是 TCB_IMGPSScanX 的各種 procedurefunction 實作。
  2. 銜接初始化區段:當上述所有檔案的內容都被「貼進來」後,程式碼剛好接到了 initialization 關鍵字。

語法邏輯如下:

implementation

// 編譯器開始貼上檔案
[貼上 UI.pas 的內容]
[貼上 Scan.pas 的內容]
[貼上 Data.pas 的內容]
[貼上 Utils.pas 的內容]

// 貼完後,剛好銜接這支單元的初始化邏輯 (AxCtrl)
initialization
  TActiveFormFactory.Create(...);
  SetLicenseKey(...);
end.

總結

  • { ... } = 註解(編譯器不看)。
  • {$I ...} = 編譯器指令(叫編譯器去別的檔案搬程式碼過來貼上)。
  • 為什麼這樣寫? 這是為了讓主檔保持簡潔。主檔只負責「宣告結構」和「啟動初始化」,而「具體的細節邏輯」則被藏在那些被 Include 的檔案裡。

10. 註解語法與風格慣例 (Comments Style)

Delphi 提供三種主要的註解方式,以及一些開發者社群常用的視覺化風格:

10.1 單行註解 (//)

  • 用法:從 // 開始到該行結束。
  • 視覺強調 (////)
    • 你在源碼中看到的 //// 語法上等同於 //
    • 開發者意圖:通常用於**「標題化」**或**「強力分隔」**。多個斜線能在視覺上形成橫條效果,幫助開發者在快速捲動程式碼時一眼認出關鍵區塊(例如://// ***** 預設區 ***** ////)。

10.2 區塊註解 ({ })

  • 用法:被 { } 包圍的所有文字。
  • 特點:常用於長篇說明的標註,或是在調試時暫時關閉一整段程式碼。
  • 編譯指令:若開頭為 {$(如 {$R *.DFM}),則代表它是給編譯器的指令,而非一般註解。

10.3 替代區塊註解 ((* *))

  • 用法:由 (* 開始到 ) 結束。
  • 主要用途 (註解嵌套):當你想要註解掉一段已經包含 { } 註解的程式碼時,Delphi 規定 { } 不能包在另一對 { } 裡面,此時必須改用 (* ... *) 來包裹,才能正確註解。

10.4 文件化註解 (/// XML Documentation)

  • 用法:在方法上方使用連續三個斜線。
  • 作用:現代 Delphi IDE (XE 以後) 會解析這類註解,並在鼠標懸停於方法名上時顯示提示說明(類似 JavaDoc 或 C# XML Doc)。

10.5 分隔線風格示例

在大型檔案(如 CB_IMGPSScanImp.pas)中,常用註解來劃分功能界線:

// ********************************************************************* //
// 這是區塊分隔線風格,常用於自動生成的 TLB 檔
// ********************************************************************* //

{-----------------------------------------------------------------------}
{  這是另一種常見的手寫分隔線風格                                          }
{-----------------------------------------------------------------------}