Delphi での名前空間の使い方
プログラムとユニット:インデックス への移動
重要:RAD Studio では、ユニット スコープ名(名前空間やユニット名に付加した接頭辞)をサポートしています。名前が完全修飾名と見なされるには、ユニット スコープ名が付いている必要があります。詳細は、「ユニット スコープ名」を参照してください。
Delphi では、ユニットは型の基本的なコンテナです。Delphi での名前空間は Delphi ユニットのコンテナになります。
従来の Delphi ユニットとは異なり、名前空間をネストさせて包含階層を構成することができます。名前空間をネストさせることで、識別子や型を編成したり、同じ名前の型を明確に区別することができます。名前空間は Delphi ユニットのコンテナなので、異なるパッケージ内にある同じ名前のユニットを区別するのに使用することもできます。
たとえば、MyNameSpace 内のクラス MyClass は、YourNamespace 内のクラス MyClass とは異なります。
このトピックで扱う内容は次のとおりです。
- プロジェクト デフォルト名前空間と名前空間宣言
- 名前空間の検索範囲
- Delphi ユニットでの名前空間の使用
名前空間の宣言
RAD Studio では、プロジェクト ファイル(program、library、package のいずれか)にプロジェクト デフォルト名前空間と呼ばれる独自の名前空間が暗黙に導入されています。ユニットは、プロジェクト デフォルト名前空間に属している場合もあれば、別の名前空間に属していると自ら明示的に宣言する場合もあります。いずれの場合も、ユニットがどの名前空間に属しているかは、unit ヘッダーで宣言されます。たとえば、次のような明示的な名前空間宣言について考えてみましょう。
unit MyCompany.MyWidgets.MyUnit;
まず、名前空間がドットで区切られていることに注目してください。名前空間では、ドットとドットの間に識別子の新しいシンボルが挿入されることはありません。ドットはユニット名の一部なのです。この例の場合、ソース ファイル名は MyCompany.MyWidgets.MyUnit.pas、コンパイル済み出力ファイル名は MyCompany.MyWidgets.MyUnit.dcu です。
次に、ドットが名前空間どうしの概念的なネスト(つまり包含)を意味していることに注目してください。上記の例では、ユニット MyUnit が MyWidgets 名前空間に属しており、その名前空間自体が MyCompany 名前空間に含まれていることを宣言しています。なお、この包含関係はあくまでドキュメントのためのものであることに注意してください。
プロジェクト デフォルト名前空間では、プロジェクト内のすべてのユニットの名前空間を宣言しています。次のような宣言を考えてみましょう。
Program MyCompany.Programs.MyProgram; Library MyCompany.Libs.MyLibrary; Package MyCompany.Packages.MyPackage;
これらの文では、program、library、package のプロジェクト デフォルト名前空間をそれぞれ設定しています。これらの宣言から最も右の識別子(とドット)を削除したものが名前空間になります。
明示的な名前空間が省略されているユニットは、汎用ユニットと呼ばれます。汎用ユニットは、自動的にプロジェクト デフォルト名前空間に属します。上記の program 宣言がある場合、次のようなユニット宣言をすると、コンパイラは MyUnit が MyCompany.Programs 名前空間に属しているとみなします。
unit MyUnit;
プロジェクト デフォルト名前空間は、汎用ユニットの Delphi ソース ファイルの名前には影響しません。上記の例の場合であれば、Delphi ソース ファイルの名前は MyUnit.pas になります。同じルールが dcu ファイル名にも当てはまります。上記の例の場合、生成される dcu ファイルは、MyUnit.dcu です。
名前空間文字列では、大文字/小文字は区別されません。コンパイラは、大文字と小文字の違いしかない 2 つの名前空間を同じものとみなします。ただし、コンパイラでは名前空間文字列の大文字と小文字をそのまま保っており、出力ファイル名、エラー メッセージ、および RTTI ユニット識別子には、元の大文字と小文字がそのまま使用されます。クラス名と型名の RTTI には、完全名前空間の指定が含まれています。
名前空間の検索
ユニットでは、依存している他のユニットを宣言する必要があります。コンパイラは、それらのユニットを調べて識別子を探す必要があります。名前空間が明示されているユニットの場合、検索範囲は既にわかっていますが、汎用ユニットの場合は、コンパイラが名前空間の検索範囲を決定する必要があります。
次のような unit と uses の宣言を考えてみましょう。
unit MyCompany.ProjectX.ProgramY.MyUnit1; uses MyCompany.Libs.Unit2, Unit3, Unit4;
これらの宣言は、MyUnit1 を MyCompany.ProjectX.ProgramY 名前空間に属しているものとして設定します。MyUnit1 は、MyCompany.Libs.Unit2 と汎用ユニット Unit3 および Unit4 という他の 3 つのユニットに依存しています。Unit2 については、uses 句で完全修飾ユニット名が指定されているため、コンパイラはこのユニット内の識別子名を解決できます。一方、Unit3 および Unit4 内の識別子名を解決するには、コンパイラは、名前空間の検索順序を決定する必要があります。
名前空間の検索順序
検索場所は、コンパイラ オプション、プロジェクト デフォルト名前空間、現在のユニットの名前空間の 3 つの考えられる情報源から決定できます。
コンパイラは、次の順序で識別子名を解決します。
- 現在のユニットの名前空間(存在する場合)
- プロジェクト デフォルト名前空間(存在する場合)
- コンパイラ オプションで指定された名前空間
名前空間の検索例
以下のサンプル プロジェクトとユニット ファイルで名前空間の検索順序の例を示します。
// Project file declarations... program MyCompany.ProjectX.ProgramY; // Unit source file declaration... unit MyCompany.ProjectX.ProgramY.MyUnit1;
このプログラム例の場合、コンパイラは次の順序で名前空間を検索します。
- MyCompany.ProjectX.ProgramY
- MyCompany.ProjectX
- コンパイラ オプションで指定された名前空間
なお、現在のユニットが汎用である(明示的な名前空間宣言が unit 文にない)場合は、プロジェクト デフォルト名前空間から解決が開始されます。
名前空間の使用
Delphi では、uses 句で指定されたモジュールは、現在のユニットのコンテキストに組み込まれます。uses 句では、モジュールを完全修飾名(つまり、完全名前空間の指定を含む)か汎用名のどちらかで参照する必要があります。後者の場合は、名前空間解決メカニズムに基づいてユニットが特定されます。
完全修飾ユニット名
以下では、名前空間を使った uses 句の例を示します。
unit MyCompany.Libs.MyUnit1; uses MyCompany.Libs.Unit2, // Fully qualified name. UnitX; // Generic name.
モジュールがいったんコンテキストに組み込まれたら、ソース コードでは、そのモジュール内の識別子を非修飾名か完全修飾名のどちらかで参照できます(後者は、異なるユニットに含まれている同じ名前の識別子を区別するために必要な場合に使用します)。次の 2 つの Writeln 文は同等です。
uses MyCompany.Libs.Unit2; begin Writeln(MyCompany.Libs.Unit2.SomeString); Writeln(SomeString); end.
完全修飾識別子には、完全名前空間の指定が含まれている必要があります。上記の例では、次のように名前空間の一部だけを使って SomeString を参照すると、エラーになります。
Writeln(Unit2.SomeString); // ERROR! Writeln(Libs.Unit2.SomeString); // ERROR! Writeln(MyCompany.Libs.Unit2.SomeString); // Correct. Writeln(SomeString); // Correct.
uses 句で名前空間の一部だけを参照すると、やはりエラーになります。名前空間に含まれているユニットやシンボルをすべてインポートする仕組みはありません。次のコードで MyCompany 名前空間内のユニットとシンボルがすべてインポートされることはありません。
uses MyCompany; // ERROR!
この制限は、with...do 文にも当てはまります。このコードはコンパイラ エラーになります。
with MyCompany.Libs do // ERROR!
マルチユニット名前空間
複数のユニット宣言で同じ名前空間を参照している場合は、複数のユニットが同じ名前空間に属することができます。たとえば、以下のユニット宣言で unit1.pas と unit2.pas の 2 つのファイルを作成できます。
// in file 'unit1.pas' unit MyCompany.ProjectX.ProgramY.Unit1 // in file 'unit2.pas' unit MyCompany.ProjectX.ProgramY.Unit2
この例では、名前空間 MyCompany.ProjectX.ProgramY に、unit1.pas および unit2.pas のすべての interface シンボルが論理的に含まれています。
名前空間内のシンボル名は、その名前空間内のすべてのユニットにわたって一意である必要があります。上記の例では、Unit1 と Unit2 の両方で mySymbol という名前のグローバル インターフェイス シンボルを定義するとエラーになります。
名前空間にまとめられた個々のユニットは、ファイルの uses 句で個々のユニットを明示的に使用しない限り、ソース コードでは使用できません。つまり、ソース ファイルで名前空間だけを使用する場合、その名前空間内のユニットに含まれているシンボルを参照する完全修飾識別子式では、そのシンボルが定義されているユニットの名前だけでなく、名前空間名も使用する必要があります。
uses 句では、名前空間の他に、その名前空間内の個々のユニットも参照できます。その場合、uses 句に列挙されている特定のユニットにあるシンボルを参照する完全修飾式は、修飾子の実際のユニット名または完全修飾名(名前空間とユニット名を含む)を使って参照できます。これら 2 つの参照形式はまったく同じ働きをし、同じシンボルを参照します。
メモ: ソース ファイルまたは dcu ファイルからコンパイルする場合にのみ、uses 句でユニットを明示的に使用することができます。名前空間内のユニットがアセンブリにコンパイルされ、個々のユニットではなくそのアセンブリがプロジェクトで参照される場合は、名前空間内のユニットを明示的に参照するソース コードはエラーになります。