インターフェイス参照(Delphi)

提供: RAD Studio
移動先: 案内検索

オブジェクト インターフェイス:インデックス への移動


インターフェイス型の変数を宣言する場合、変数はそのインターフェイスを実装する任意のクラスのインスタンスを参照できます。 以下のトピックでは、インターフェイス参照について説明し、関連トピックを示します。

インターフェイス参照の実装

インターフェイス参照変数により、コンパイル時にはどこに実装されているかわからないインターフェイス メソッドを呼び出せます。 ただし、次の条件に従います。

  • インターフェイス型の式は、インターフェイスで宣言されたメソッドおよびプロパティへのアクセスのみを提供し、実装するクラスのその他のメンバにはアクセスできません。
  • オブジェクトのクラスが下位のインターフェイスを実装している場合、クラス(または継承元のクラス)が上位インターフェイスも明示的に実装しているのでなければ、インターフェイス型の式からそのオブジェクトは参照できません。

例:

 type
   IAncestor = interface
   end;
   IDescendant = interface(IAncestor)
     procedure P1;
   end;
   TSomething = class(TInterfacedObject, IDescendant)
     procedure P1;
     procedure P2;
   end;
      // ...
 var
   D: IDescendant;
   A: IAncestor;
 begin
   D := TSomething.Create;  // works!
   A := TSomething.Create;  // error
   D.P1;  // works!
   D.P2;  // error
 end;

この例では、A は IAncestor 型の変数として宣言されています。 TSomething では、実装するインタ-フェイス間の IAncestor をリストしないため、TSomething インスタンスを A に割り当てることはできません。 ただし、TSomething の宣言を次のように変更すると、

 TSomething = class(TInterfacedObject, IAncestor, IDescendant)
  // ...

最初のエラーは有効な代入となります。 D は IDescendant 型の変数として宣言されています。 D は TSomething のインスタンスを参照しますが、P2 は IDescendant のメソッドではないので、それを使用して TSomething の P2 メソッドにはアクセスできません。 ただし、D の宣言を次のように変更すると、

 D: TSomething;

第 2 のエラーは有効なメソッド呼び出しとなります。

Win32 プラットフォームでは、通常、インターフェイス参照は参照カウントによって管理され、System.IInterface から継承した _AddRef メソッド および _Release メソッドに依存します。 参照カウントのデフォルト実装を使用して、オブジェクトがインターフェイス経由でのみ参照される場合には、手動でオブジェクトを破棄する必要はありません。オブジェクトはそれに対する最後の参照がスコープから外れると自動的に破棄されます。 クラスによっては、このデフォルトの存続期間管理を省略するためにインターフェイスを実装するものがあり、また、オブジェクトに所有者がない場合に限って参照カウントを使用するハイブリッド オブジェクトもあります。

グローバル インターフェイス型変数は、nil にのみ初期化できます。

インターフェイス型の式がオブジェクトを参照するかどうかを判断するため、標準関数 Assigned に渡します。

インターフェイス代入の互換性

指定されたクラス型の変数は、そのクラスによって実装された任意のインターフェイス型と代入互換性があります。 インターフェイス型の変数は、任意の上位インターフェイス型と代入互換性があります。 値 nil は、任意のインターフェイス型変数に代入できます。

インターフェイス型の式は、バリアントに代入できます。 インターフェイスが IDispatch 型または下位クラスの場合、バリアントが受け取る型コードは varDispatch です。 そうでない場合、バリアントが受け取る型コードは varUnknown です。

型コードが varEmptyvarUnknown、または varDispatch のバリアントは、IInterface 変数に代入できます。 型コードが varEmpty、または varDispatch のバリアントは、IDispatch 変数に代入できます。

インターフェイス型キャスト

インターフェイス型の式は、バリアントにキャストできます。 インターフェイスが IDispatch 型または下位クラスの場合、返されるバリアントの型コードは varDispatch です。 そうでない場合、返されるバリアントの型コードは varUnknown です。

型コードが varEmptyvarUnknown、または varDispatch のバリアントは、IInterface にキャストできます。 型コードが varEmpty、または varDispatch のバリアントは、IDispatch にキャストできます。

インターフェイス問い合わせ

as 演算子を使用して、確認済みインターフェイス型キャストを実行できます。 これをインターフェイス問い合わせとも言い、実際(実行時)のオブジェクトの型に基づいて、オブジェクト参照またはその他のインターフェイス参照からインターフェイス型の式を生じます。 インターフェイス問い合わせは次の形式をとります。

object as interface

このとき、object はインターフェイス型またはバリアント型の式であるか、インターフェイスを実装するクラスのインスタンスを表し、interface は GUID により宣言された任意のインターフェイスです。

オブジェクトが nil の場合、インターフェイス問い合わせは nil を返します。 そうでない場合は、インターフェイスの GUID をオブジェクト内の QueryInterface メソッドに渡し、QueryInterface がゼロを戻さなければ例外を発生させます。 QueryInterface がゼロを返す(オブジェクトのクラスがそのインターフェイスを実装することを示す)場合、インターフェイス問い合わせはオブジェクトへのインターフェイス参照を返します。

オブジェクトへのインターフェイス参照のキャスト

as 演算子は、インターフェイス参照の取得元であるオブジェクトへの逆のインターフェイス参照をキャストするためにも使用できます。 このキャストは、Delphi オブジェクトから取得したインターフェイスにのみ機能します。 例:

 var
   LIntfRef: IInterface;
   LObj: TInterfacedObject;
 begin
   { Create an interfaced object and extract an interface from it. }
   LIntfRef := TInterfacedObject.Create();
 
   { Cast the interface back to the original object. }
   LObj := LIntfRef as TInterfacedObject;
 end;

上記の例は、インターフェイス参照を取得した元のオブジェクトの取得方法を示しています。 この手法は、インターフェイス参照の処理がそれだけでは不十分な場合に役立ちます。

インターフェイスが指定されたクラスから抽出したものではなかった場合、as 演算子は例外を発生させます。

 var
   LIntfRef: IInterface;
   LObj: TInterfacedObject;
 begin
   { Create an interfaced object and extract an interface from it. }
   LIntfRef := TInterfacedObject.Create();
 
   try
     { Cast the interface to a TComponent. }
     LObj := LIntfRef as TComponent;
   except
     Writeln('LIntfRef was not referencing a TComponent instance');
   end;  
 end;

オブジェクトへのインターフェイス参照から(安全でない)通常型のキャストも実行できます。 オブジェクトの安全でないキャストの場合と同様、このメソッドは例外を発生させません。 オブジェクトからオブジェクトへの安全でないキャストと、インターフェイスからオブジェクトへの安全でないキャストとの違いは、互換性のない型の場合には前者は有効なポインタを返すのに対し、後者は nil を返すことです。 この例は、安全でないキャストの使用方法を示します。

 var
   LIntfRef: IInterface;
   LObj: TInterfacedObject;
 begin
   { Create an interfaced object and extract an interface from it. }
   LIntfRef := TInterfacedObject.Create();
 
   { Cast the interface to a TComponent. }
   LObj := TComponent(LIntfRef);
 
   if LObj = nil then
     Writeln('LIntfRef was not referencing a TComponent instance');
 
   { Cast the interface to a TObject. }
   LObj := TObject(LIntfRef);
 
   if LObj <> nil then
     Writeln('LIntfRef was referencing a TObject (or descendant).');
 end;

nil 参照となる可能性を回避するには、is 演算子を使用し、インターフェイス参照が指定されたクラスから抽出したものかどうかを確認します。

 if Intf is TCustomObject then ...

メモ: 安全でないキャスト、または as 演算子や is 演算子を使用している場合は、Delphi 限定のオブジェクトを使用していることを確認してください。

関連項目