單元間互相參照

觀念說明

各單元之間可互相依賴、參照。當一個單元參照另一個單元時,被參照的單元可以宣告於interface介面區段,也能僅寫在implementation實作區段,兩者差異如下:

寫在 interface(介面區參照) 可依據被參照單元的內容,對外定義全新的公開元件(程序、自訂型別等),專案內所有其他單元皆可存取。

僅寫在 implementation(實作區參照) 使用範圍受限:被參照單元內的識別字,僅能於當前單元的實作程式碼中使用,外部無法存取。

範例

以下僅作示範,讀者尚未學習類別 class,只需先理解觀念即可。

新增單元檔 Classes.pas

unit Classes;
{$mode objfpc}{$H+}
interface
type
  TObject=class;
  TComponent=class(TObject)
    constructor Create(AOwner:TComponent);virtual;
  end;
  TObject=class
    destructor Destroy;virtual;
  end;
implementation
constructor TComponent.Create(AOwner:TComponent);begin end;
destructor TObject.Destroy;begin end;
end.

接著於單元 AnotherUnit.pas 中進行參照

unit AnotherUnit;
{$ifdef FPC} {$mode objfpc}{$H+}{$J-} {$endif}

interface
uses
  Classes;
// TComponent 類別定義在 Classes 單元,因此介面區必須參照 Classes
procedure DoSomethingWithComponent(var C: TComponent);

implementation
uses SysUtils; // 僅在實作區參照 SysUtils

procedure DoSomethingWithComponent(var C: TComponent);
begin
  // FreeAndNil 來自 SysUtils,僅在實作程式內呼叫,只需於 implementation 區參照即可
  FreeAndNil(C);
end;

end.Code language: JavaScript (javascript)

注意事項

1 介面區禁止雙向循環相依

不允許兩個單元同時於各自的 interface 區段互相參照對方

原理:編譯器解析單元介面時,必須先完整解析該單元uses介面清單內所有相依單元。Pascal 嚴格遵循此規則,帶來兩大優勢:

編譯速度更快;編譯器可自動判斷哪些單元需要重新編譯,不需手動維護複雜的 Makefile 編譯腳本;修改程式碼後無須重編整個專案,就能確保所有相依項目同步更新。

2 實作區允許循環相依

只要至少其中一方僅在implementation實作區參照另一方,循環相依即為合法。

範例:單元 A 在介面區參照單元 B,單元 B 只在實作區參照單元 A,此類雙向循環架構編譯器完全支援。

發佈留言

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