解説
ユニット同士は相互に依存・参照することができます。一方のユニットが別のユニットを参照する際、参照先ユニットはinterfaceインターフェース部に記述するか、implementation実装部のみに記述する二通りの方法があり、それぞれの違いは以下の通りです。
interface部に記述(インターフェース側参照):参照先ユニットの要素をもとに、外部に公開する新しい要素(プロシージャ、独自型など)を定義可能で、プロジェクト内のすべてのユニットからアクセスできます。
implementation部のみに記述(実装側参照):利用範囲が制限されます。参照先ユニットの識別子は現在のユニットの実装コード内でのみ使用でき、外部からは参照できません。
例
これはあくまでデモ用のコードです。読者はまだクラスを学習していないため、まず概念だけ理解してください。
新規ユニットファイル 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を参照する双方向循環構造は、コンパイラが完全に対応しています。